Line data Source code
1 : /* Copyright (C) 2000 The PARI group.
2 :
3 : This file is part of the PARI/GP package.
4 :
5 : PARI/GP is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 2 of the License, or (at your option) any later
8 : version. It is distributed in the hope that it will be useful, but WITHOUT
9 : ANY WARRANTY WHATSOEVER.
10 :
11 : Check the License for details. You should have received a copy of it, along
12 : with the package; see the file 'COPYING'. If not, write to the Free Software
13 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
14 :
15 : /*******************************************************************/
16 : /** **/
17 : /** INPUT/OUTPUT SUBROUTINES **/
18 : /** **/
19 : /*******************************************************************/
20 : #ifdef _WIN32
21 : #include "../systems/mingw/pwinver.h"
22 : #include <windows.h>
23 : #include <process.h> /* for getpid */
24 : #include <fcntl.h>
25 : #include <io.h> /* for setmode */
26 : #include "../systems/mingw/mingw.h"
27 : #endif
28 : #include "paricfg.h"
29 : #ifdef HAS_STAT
30 : #include <sys/stat.h>
31 : #elif defined(HAS_OPENDIR)
32 : #include <dirent.h>
33 : #endif
34 : #ifdef HAS_WAITPID
35 : # include <sys/wait.h>
36 : #endif
37 :
38 : #include "pari.h"
39 : #include "paripriv.h"
40 : #include "anal.h"
41 : #ifdef __EMSCRIPTEN__
42 : #include "../systems/emscripten/emscripten.h"
43 : #endif
44 :
45 : #define DEBUGLEVEL DEBUGLEVEL_io
46 :
47 : typedef void (*OUT_FUN)(GEN, pariout_t *, pari_str *);
48 :
49 : static void bruti_sign(GEN g, pariout_t *T, pari_str *S, int addsign);
50 : static void matbruti(GEN g, pariout_t *T, pari_str *S);
51 : static void texi_sign(GEN g, pariout_t *T, pari_str *S, int addsign);
52 :
53 : static void
54 1191357 : bruti(GEN g, pariout_t *T, pari_str *S) { bruti_sign(g,T,S,1); }
55 : static void
56 319 : texi(GEN g, pariout_t *T, pari_str *S) { texi_sign(g,T,S,1); }
57 :
58 : void
59 0 : pari_ask_confirm(const char *s)
60 : {
61 0 : if (!cb_pari_ask_confirm)
62 0 : pari_err(e_MISC,"Can't ask for confirmation. Please define cb_pari_ask_confirm()");
63 0 : cb_pari_ask_confirm(s);
64 0 : }
65 :
66 : static char *
67 0 : strip_last_nl(char *s)
68 : {
69 0 : ulong l = strlen(s);
70 : char *t;
71 0 : if (l && s[l-1] != '\n') return s;
72 0 : if (l>1 && s[l-2] == '\r') l--;
73 0 : t = stack_malloc(l); memcpy(t, s, l-1); t[l-1] = 0;
74 0 : return t;
75 : }
76 :
77 : /********************************************************************/
78 : /** **/
79 : /** INPUT FILTER **/
80 : /** **/
81 : /********************************************************************/
82 : #define ONE_LINE_COMMENT 2
83 : #define MULTI_LINE_COMMENT 1
84 : #define LBRACE '{'
85 : #define RBRACE '}'
86 :
87 : static int
88 2449 : in_help(filtre_t *F)
89 : {
90 : char c;
91 2449 : if (!F->buf) return (*F->s == '?');
92 2442 : c = *F->buf->buf;
93 2442 : return c? (c == '?'): (*F->s == '?');
94 : }
95 : /* Filter F->s into F->t */
96 : static char *
97 1009337 : filtre0(filtre_t *F)
98 : {
99 1009337 : const char *s = F->s;
100 1009337 : char c, *t = F->t;
101 1009337 : int doctest = GP_DATA->doctest, more_input = F->more_input;
102 :
103 1009337 : if (odd(more_input)) F->more_input = 0;
104 1009337 : if (doctest && more_input <= 1)
105 : {
106 0 : int is_doctest = isspace((unsigned char)*s);
107 0 : while (isspace((unsigned char)*s)) s++;
108 0 : if (*s==0) goto END;
109 0 : if (*s=='?' && s[1]==' ') s+=2;
110 0 : else if (is_doctest)
111 0 : F->in_comment = ONE_LINE_COMMENT;
112 : }
113 253433926 : while ((c = *s++))
114 : {
115 252425331 : if (F->in_string)
116 : {
117 30149860 : *t++ = c; /* copy verbatim */
118 30149860 : switch(c)
119 : {
120 648 : case '\\': /* in strings, \ is the escape character */
121 648 : if (*s) *t++ = *s++;
122 648 : break;
123 :
124 3822417 : case '"': F->in_string = 0;
125 : }
126 30149860 : continue;
127 : }
128 :
129 222275471 : if (F->in_comment)
130 : { /* look for comment's end */
131 8937 : if (F->in_comment == MULTI_LINE_COMMENT)
132 : {
133 36943 : while (c != '*' || *s != '/')
134 : {
135 36247 : if (!*s)
136 : {
137 406 : if (!F->more_input) F->more_input = 1;
138 406 : goto END;
139 : }
140 35841 : c = *s++;
141 : }
142 696 : s++;
143 : }
144 : else
145 130645 : while (c != '\n' && *s) c = *s++;
146 8531 : F->in_comment = 0;
147 8531 : continue;
148 : }
149 :
150 : /* weed out comments and spaces */
151 222266534 : if (c=='\\' && *s=='\\') { F->in_comment = ONE_LINE_COMMENT; continue; }
152 222258699 : if (isspace((unsigned char)c)) continue;
153 220160771 : *t++ = c;
154 220160771 : switch(c)
155 : {
156 906404 : case '/':
157 906404 : if (*s == '*') { t--; F->in_comment = MULTI_LINE_COMMENT; }
158 906404 : break;
159 :
160 1086 : case '\\':
161 1086 : if (!*s) {
162 7 : if (in_help(F)) break; /* '?...\' */
163 7 : t--;
164 7 : if (!F->more_input) F->more_input = 1;
165 7 : goto END;
166 : }
167 1079 : if (*s == '\r') s++; /* DOS */
168 1079 : if (*s == '\n') {
169 336 : if (in_help(F)) break; /* '?...\' */
170 329 : t--; s++;
171 329 : if (!*s)
172 : {
173 329 : if (!F->more_input) F->more_input = 3;
174 329 : goto END;
175 : }
176 : } /* skip \<CR> */
177 743 : break;
178 :
179 3822417 : case '"': F->in_string = 1;
180 3822417 : break;
181 :
182 3567 : case LBRACE:
183 3567 : t--;
184 3567 : if (F->wait_for_brace) pari_err(e_MISC,"nested braces are not allowed");
185 3567 : F->more_input = 2;
186 3567 : F->wait_for_brace = 1;
187 3567 : break;
188 :
189 3567 : case RBRACE:
190 3567 : if (!F->wait_for_brace) pari_err(e_MISC,"unexpected closing brace");
191 3567 : F->more_input = 0; t--;
192 3567 : F->wait_for_brace = 0;
193 3567 : break;
194 : }
195 : }
196 :
197 1008595 : if (t != F->t) /* non empty input */
198 : {
199 981413 : c = t[-1]; /* last char */
200 981413 : if (c == '=') { if (!in_help(F)) F->more_input = 2; }
201 979307 : else if (! F->wait_for_brace) F->more_input = 0;
202 43436 : else if (c == RBRACE) { F->more_input = 0; t--; F->wait_for_brace--;}
203 : }
204 :
205 70618 : END:
206 1009337 : F->end = t; *t = 0; return F->t;
207 : }
208 : #undef ONE_LINE_COMMENT
209 : #undef MULTI_LINE_COMMENT
210 :
211 : char *
212 11039 : gp_filter(const char *s)
213 : {
214 : filtre_t T;
215 11039 : T.buf = NULL;
216 11039 : T.s = s;
217 11039 : T.t = (char*)stack_malloc(strlen(s)+1);
218 11039 : T.in_string = 0; T.more_input = 0;
219 11039 : T.in_comment= 0; T.wait_for_brace = 0;
220 11039 : return filtre0(&T);
221 : }
222 :
223 : void
224 806550 : init_filtre(filtre_t *F, Buffer *buf)
225 : {
226 806550 : F->buf = buf;
227 806550 : F->in_string = 0;
228 806550 : F->in_comment = 0;
229 806550 : }
230 :
231 : /********************************************************************/
232 : /** **/
233 : /** INPUT METHODS **/
234 : /** **/
235 : /********************************************************************/
236 : /* create */
237 : Buffer *
238 11265 : new_buffer(void)
239 : {
240 11265 : Buffer *b = (Buffer*) pari_malloc(sizeof(Buffer));
241 11265 : b->len = 1024;
242 11265 : b->buf = (char*)pari_malloc(b->len);
243 11265 : b->buf[0] = 0;
244 11265 : return b;
245 : }
246 : /* delete */
247 : void
248 11265 : delete_buffer(Buffer *b)
249 : {
250 11265 : if (!b) return;
251 11265 : pari_free((void*)b->buf); pari_free((void*)b);
252 : }
253 : /* resize */
254 : void
255 7434 : fix_buffer(Buffer *b, long newlbuf)
256 : {
257 7434 : b->len = newlbuf;
258 7434 : pari_realloc_ip((void**)&b->buf, b->len);
259 7434 : }
260 :
261 : static int
262 804589 : gp_read_stream_buf(FILE *fi, Buffer *b)
263 : {
264 : input_method IM;
265 : filtre_t F;
266 :
267 804589 : init_filtre(&F, b);
268 :
269 804589 : IM.file = (void*)fi;
270 804589 : IM.myfgets = (fgets_t)&fgets;
271 804589 : IM.getline = &file_input;
272 804589 : IM.free = 0;
273 804589 : return input_loop(&F,&IM);
274 : }
275 :
276 : GEN
277 8887 : gp_read_stream(FILE *fi)
278 : {
279 8887 : Buffer *b = new_buffer();
280 8887 : GEN x = NULL;
281 8887 : while (gp_read_stream_buf(fi, b))
282 : {
283 8887 : if (*(b->buf)) { x = readseq(b->buf); break; }
284 : }
285 8887 : delete_buffer(b); return x;
286 : }
287 :
288 : static GEN
289 0 : gp_read_from_input(input_method* IM, int loop, char *last)
290 : {
291 0 : Buffer *b = new_buffer();
292 0 : GEN x = NULL;
293 : filtre_t F;
294 0 : if (last) *last = 0;
295 : for (;;)
296 0 : {
297 : char *s;
298 0 : init_filtre(&F, b);
299 0 : if (!input_loop(&F, IM)) break;
300 0 : s = b->buf;
301 0 : if (s[0])
302 : {
303 0 : if (gp_meta(s,0)) continue;
304 0 : x = closure_evalres(pari_compile_str(s));
305 0 : if (last) *last = s[strlen(s) - 1];
306 : }
307 0 : if (!loop) break;
308 : }
309 0 : delete_buffer(b);
310 0 : return x;
311 : }
312 :
313 : GEN
314 19 : gp_read_file(const char *s)
315 : {
316 19 : GEN x = gnil;
317 19 : FILE *f = switchin(s);
318 12 : if (file_is_binary(f))
319 : {
320 12 : x = readbin(s,f, NULL);
321 12 : if (!x) pari_err_FILE("input file",s);
322 : }
323 : else {
324 0 : pari_sp av = avma;
325 0 : Buffer *b = new_buffer();
326 0 : x = gnil;
327 : for (;;) {
328 0 : if (!gp_read_stream_buf(f, b)) break;
329 0 : if (!strcmp(b->buf,"\\qf")) break;
330 0 : if (*(b->buf)) { set_avma(av); x = readseq(b->buf); }
331 : }
332 0 : delete_buffer(b);
333 : }
334 12 : popinfile(); return x;
335 : }
336 :
337 : static char*
338 0 : string_gets(char *s, int size, const char **ptr)
339 : {
340 : /* f is actually a const char** */
341 0 : const char *in = *ptr;
342 : int i;
343 : char c;
344 :
345 : /* Copy from in to s */
346 0 : for (i = 0; i+1 < size && in[i] != 0;)
347 : {
348 0 : s[i] = c = in[i]; i++;
349 0 : if (c == '\n') break;
350 : }
351 0 : s[i] = 0; /* Terminating 0 byte */
352 0 : if (i == 0) return NULL;
353 :
354 0 : *ptr += i;
355 0 : return s;
356 : }
357 :
358 : GEN
359 0 : gp_read_str_multiline(const char *s, char *last)
360 : {
361 : input_method IM;
362 0 : const char *ptr = s;
363 : GEN z;
364 :
365 0 : IM.file = (void*)(&ptr);
366 0 : IM.myfgets = (fgets_t)&string_gets;
367 0 : IM.getline = &file_input;
368 0 : IM.free = 0;
369 :
370 0 : z = gp_read_from_input(&IM, 1, last);
371 0 : return z ? z: gnil;
372 : }
373 :
374 : static void
375 0 : gp_read_str_history(const char *s)
376 : {
377 : input_method IM;
378 0 : const char *ptr = s;
379 0 : char last = 0;
380 0 : pari_sp av = avma;
381 0 : IM.file = (void*)(&ptr);
382 0 : IM.myfgets = (fgets_t)&string_gets;
383 0 : IM.getline = &file_input;
384 0 : IM.free = 0;
385 0 : for(;ptr[0];)
386 : {
387 : GEN z;
388 0 : timer_start(GP_DATA->T);
389 0 : walltimer_start(GP_DATA->Tw);
390 0 : pari_set_last_newline(1);
391 0 : z = gp_read_from_input(&IM, 0, &last);
392 0 : pari_alarm(0);
393 0 : if (!pari_last_was_newline()) pari_putc('\n');
394 0 : if (z)
395 : {
396 0 : long t = timer_delay(GP_DATA->T);
397 0 : long r = walltimer_delay(GP_DATA->Tw);
398 0 : if (t && GP_DATA->chrono)
399 : {
400 0 : if (pari_mt_nbthreads==1)
401 : {
402 0 : pari_puts("time = ");
403 0 : pari_puts(gp_format_time(t));
404 : }
405 : else
406 : {
407 0 : pari_puts("cpu time = ");
408 0 : pari_puts(gp_format_time(t));
409 0 : pari_puts(", real time = ");
410 0 : pari_puts(gp_format_time(r));
411 : }
412 0 : pari_puts(".\n");
413 : }
414 0 : if (GP_DATA->simplify) z = simplify_shallow(z);
415 0 : pari_add_hist(z, t, r);
416 0 : if (z != gnil && last!=';')
417 0 : gp_display_hist(pari_nb_hist());
418 : }
419 0 : set_avma(av);
420 0 : parivstack_reset();
421 : }
422 0 : }
423 :
424 : void
425 0 : gp_embedded_init(long rsize, long vsize)
426 : {
427 0 : pari_init(rsize, 1UL<<20);
428 0 : paristack_setsize(rsize, vsize);
429 0 : }
430 :
431 : long
432 0 : gp_embedded(const char *s)
433 : {
434 0 : long err = 0;
435 : struct gp_context state;
436 0 : gp_context_save(&state);
437 0 : timer_start(GP_DATA->T);
438 0 : timer_start(GP_DATA->Tw);
439 0 : pari_set_last_newline(1);
440 0 : pari_CATCH(CATCH_ALL)
441 : {
442 0 : pari_err_display(pari_err_last());
443 0 : err_printf("\n");
444 0 : gp_context_restore(&state);
445 0 : err = 1;
446 : } pari_TRY {
447 0 : gp_read_str_history(s);
448 0 : } pari_ENDCATCH;
449 0 : if (!pari_last_was_newline()) pari_putc('\n');
450 0 : set_avma(pari_mainstack->top);
451 0 : return err;
452 : }
453 :
454 : GEN
455 305 : gp_readvec_stream(FILE *fi)
456 : {
457 305 : pari_sp ltop = avma;
458 305 : Buffer *b = new_buffer();
459 305 : long i = 1, n = 16;
460 305 : GEN z = cgetg(n+1,t_VEC);
461 : for(;;)
462 : {
463 795646 : if (!gp_read_stream_buf(fi, b)) break;
464 795341 : if (!*(b->buf)) continue;
465 795341 : if (i>n)
466 : {
467 2149 : if (DEBUGLEVEL) err_printf("gp_readvec_stream: reaching %ld entries\n",n);
468 2149 : n <<= 1;
469 2149 : z = vec_lengthen(z,n);
470 : }
471 795341 : gel(z,i++) = readseq(b->buf);
472 : }
473 305 : if (DEBUGLEVEL) err_printf("gp_readvec_stream: found %ld entries\n",i-1);
474 305 : setlg(z,i); delete_buffer(b);
475 305 : return gc_GEN(ltop,z);
476 : }
477 :
478 : GEN
479 4 : gp_readvec_file(const char *s)
480 : {
481 4 : GEN x = NULL;
482 4 : FILE *f = switchin(s);
483 4 : if (file_is_binary(f)) {
484 : int junk;
485 0 : x = readbin(s,f,&junk);
486 0 : if (!x) pari_err_FILE("input file",s);
487 : } else
488 4 : x = gp_readvec_stream(f);
489 4 : popinfile(); return x;
490 : }
491 :
492 : char *
493 1000578 : file_getline(Buffer *b, char **s0, input_method *IM)
494 : {
495 1000578 : const ulong MAX = (1UL << 31) - 1;
496 : ulong used0, used;
497 :
498 1000578 : **s0 = 0; /* paranoia */
499 1000578 : used0 = used = *s0 - b->buf;
500 : for(;;)
501 7021 : {
502 1007599 : ulong left = b->len - used, l, read;
503 : char *s;
504 :
505 : /* If little space left, double the buffer size before next read. */
506 1007599 : if (left < 512)
507 : {
508 7420 : fix_buffer(b, b->len << 1);
509 7420 : left = b->len - used;
510 7420 : *s0 = b->buf + used0;
511 : }
512 : /* # of chars read by fgets is an int; be careful */
513 1007599 : read = minuu(left, MAX);
514 1007599 : s = b->buf + used;
515 1007599 : if (! IM->myfgets(s, (int)read, IM->file)) return **s0? *s0: NULL; /* EOF */
516 :
517 1005383 : l = strlen(s);
518 1005383 : if (l+1 < read || s[l-1] == '\n') return *s0; /* \n */
519 7021 : used += l;
520 : }
521 : }
522 :
523 : /* Read from file (up to '\n' or EOF) and copy at s0 (points in b->buf) */
524 : char *
525 1000502 : file_input(char **s0, int junk, input_method *IM, filtre_t *F)
526 : {
527 : (void)junk;
528 1000502 : return file_getline(F->buf, s0, IM);
529 : }
530 :
531 : static void
532 2204 : runaway_close(filtre_t *F)
533 : {
534 2204 : if (F->in_string)
535 : {
536 0 : pari_warn(warner,"run-away string. Closing it");
537 0 : F->in_string = 0;
538 : }
539 2204 : if (F->in_comment)
540 : {
541 0 : pari_warn(warner,"run-away comment. Closing it");
542 0 : F->in_comment = 0;
543 : }
544 2204 : }
545 : /* Read a "complete line" and filter it. Return: 0 if EOF, 1 otherwise */
546 : int
547 951064 : input_loop(filtre_t *F, input_method *IM)
548 : {
549 951064 : Buffer *b = (Buffer*)F->buf;
550 951064 : char *to_read, *s = b->buf;
551 :
552 : /* read first line */
553 951064 : if (! (to_read = IM->getline(&s,1, IM, F)) ) { runaway_close(F); return 0; }
554 :
555 : /* buffer is not empty, init filter */
556 948860 : F->in_string = 0;
557 948860 : F->more_input= 0;
558 948860 : F->wait_for_brace = 0;
559 : for(;;)
560 : {
561 998298 : if (GP_DATA->echo == 2) gp_echo_and_log("", strip_last_nl(to_read));
562 998298 : F->s = to_read;
563 998298 : F->t = s;
564 998298 : (void)filtre0(F); /* pre-processing of line, read by previous call to IM->getline */
565 998298 : if (IM->free) pari_free(to_read);
566 998298 : if (! F->more_input) break;
567 :
568 : /* read continuation line */
569 49438 : s = F->end;
570 49438 : to_read = IM->getline(&s,0, IM, F);
571 49438 : if (!to_read)
572 : {
573 0 : if (!*(b->buf)) runaway_close(F);
574 0 : break;
575 : }
576 : }
577 948860 : return 1;
578 : }
579 :
580 : /********************************************************************/
581 : /** **/
582 : /** GENERAL PURPOSE PRINTING **/
583 : /** **/
584 : /********************************************************************/
585 : PariOUT *pariOut, *pariErr;
586 : static void
587 311487 : _fputs(const char *s, FILE *f ) {
588 : #ifdef _WIN32
589 : win32_ansi_fputs(s, f);
590 : #else
591 311487 : fputs(s, f);
592 : #endif
593 311487 : }
594 : static void
595 10855991 : _putc_log(char c) { if (pari_logfile) (void)putc(c, pari_logfile); }
596 : static void
597 311487 : _puts_log(const char *s)
598 : {
599 311487 : FILE *f = pari_logfile;
600 : const char *p;
601 311487 : if (!f) return;
602 0 : if (pari_logstyle != logstyle_color)
603 0 : while ( (p = strchr(s, '\x1b')) )
604 : { /* skip ANSI color escape sequence */
605 0 : if ( p!=s ) fwrite(s, 1, p-s, f);
606 0 : s = strchr(p, 'm');
607 0 : if (!s) return;
608 0 : s++;
609 : }
610 0 : fputs(s, f);
611 : }
612 : static void
613 261146 : _flush_log(void)
614 261146 : { if (pari_logfile != NULL) (void)fflush(pari_logfile); }
615 :
616 : static void
617 10231235 : normalOutC(char c) { putc(c, pari_outfile); _putc_log(c); }
618 : static void
619 89 : normalOutS(const char *s) { _fputs(s, pari_outfile); _puts_log(s); }
620 : static void
621 219992 : normalOutF(void) { fflush(pari_outfile); _flush_log(); }
622 : static PariOUT defaultOut = {normalOutC, normalOutS, normalOutF};
623 :
624 : static void
625 624756 : normalErrC(char c) { putc(c, pari_errfile); _putc_log(c); }
626 : static void
627 311398 : normalErrS(const char *s) { _fputs(s, pari_errfile); _puts_log(s); }
628 : static void
629 41154 : normalErrF(void) { fflush(pari_errfile); _flush_log(); }
630 : static PariOUT defaultErr = {normalErrC, normalErrS, normalErrF};
631 :
632 : /** GENERIC PRINTING **/
633 : void
634 1895 : resetout(int initerr)
635 : {
636 1895 : pariOut = &defaultOut;
637 1895 : if (initerr) pariErr = &defaultErr;
638 1895 : }
639 : void
640 1895 : initout(int initerr)
641 : {
642 1895 : pari_infile = stdin;
643 1895 : pari_outfile = stdout;
644 1895 : pari_errfile = stderr;
645 1895 : resetout(initerr);
646 1895 : }
647 :
648 : static int last_was_newline = 1;
649 :
650 : static void
651 1164171 : set_last_newline(char c) { last_was_newline = (c == '\n'); }
652 :
653 : void
654 731230 : out_putc(PariOUT *out, char c) { set_last_newline(c); out->putch(c); }
655 : void
656 104891 : pari_putc(char c) { out_putc(pariOut, c); }
657 :
658 : void
659 435622 : out_puts(PariOUT *out, const char *s) {
660 435622 : if (*s) { set_last_newline(s[strlen(s)-1]); out->puts(s); }
661 435622 : }
662 : void
663 62479 : pari_puts(const char *s) { out_puts(pariOut, s); }
664 :
665 : int
666 122649 : pari_last_was_newline(void) { return last_was_newline; }
667 : void
668 151145 : pari_set_last_newline(int last) { last_was_newline = last; }
669 :
670 : void
671 205953 : pari_flush(void) { pariOut->flush(); }
672 :
673 : void
674 0 : err_flush(void) { pariErr->flush(); }
675 :
676 : static GEN
677 12 : log10_2(void)
678 12 : { return divrr(mplog2(LOWDEFAULTPREC), mplog(utor(10,LOWDEFAULTPREC))); }
679 :
680 : /* e binary exponent, return exponent in base ten */
681 : static long
682 177729 : ex10(long e) {
683 : pari_sp av;
684 : GEN z;
685 177729 : if (e >= 0) {
686 172454 : if (e < 1e15) return (long)(e*LOG10_2);
687 6 : av = avma; z = mulur(e, log10_2());
688 6 : z = floorr(z); e = itos(z);
689 : }
690 : else /* e < 0 */
691 : {
692 5275 : if (e > -1e15) return (long)(-(-e*LOG10_2)-1);
693 6 : av = avma; z = mulsr(e, log10_2());
694 6 : z = floorr(z); e = itos(z) - 1;
695 : }
696 12 : return gc_long(av, e);
697 : }
698 :
699 : static char *
700 23408 : zeros(char *b, long nb) { while (nb-- > 0) *b++ = '0'; *b = 0; return b; }
701 :
702 : /* # of decimal digits, assume l > 0 */
703 : static long
704 770193 : numdig(ulong l)
705 : {
706 770193 : if (l < 100000)
707 : {
708 720328 : if (l < 100) return (l < 10)? 1: 2;
709 323855 : if (l < 10000) return (l < 1000)? 3: 4;
710 121522 : return 5;
711 : }
712 49865 : if (l < 10000000) return (l < 1000000)? 6: 7;
713 17879 : if (l < 1000000000) return (l < 100000000)? 8: 9;
714 0 : return 10;
715 : }
716 :
717 : /* let ndig <= 9, x < 10^ndig, write in p[-ndig..-1] the decimal digits of x */
718 : static void
719 1167568 : utodec(char *p, ulong x, long ndig)
720 : {
721 1167568 : switch(ndig)
722 : {
723 404940 : case 9: *--p = x % 10 + '0'; x = x/10;
724 415254 : case 8: *--p = x % 10 + '0'; x = x/10;
725 429678 : case 7: *--p = x % 10 + '0'; x = x/10;
726 447240 : case 6: *--p = x % 10 + '0'; x = x/10;
727 568762 : case 5: *--p = x % 10 + '0'; x = x/10;
728 661639 : case 4: *--p = x % 10 + '0'; x = x/10;
729 771095 : case 3: *--p = x % 10 + '0'; x = x/10;
730 907630 : case 2: *--p = x % 10 + '0'; x = x/10;
731 1167568 : case 1: *--p = x % 10 + '0'; x = x/10;
732 : }
733 1167568 : }
734 :
735 : /* convert abs(x) != 0 to str. Prepend '-' if (sx < 0) */
736 : static char *
737 770193 : itostr_sign(GEN x, int sx, long *len)
738 : {
739 : long l, d;
740 770193 : ulong *res = convi(x, &l);
741 : /* l 9-digits words (< 10^9) + (optional) sign + \0 */
742 770193 : char *s = (char*)new_chunk(nchar2nlong(l*9 + 1 + 1)), *t = s;
743 :
744 770193 : if (sx < 0) *t++ = '-';
745 770193 : d = numdig(*--res); t += d; utodec(t, *res, d);
746 1167568 : while (--l > 0) { t += 9; utodec(t, *--res, 9); }
747 770193 : *t = 0; *len = t - s; return s;
748 : }
749 :
750 : /********************************************************************/
751 : /** **/
752 : /** WRITE A REAL NUMBER **/
753 : /** **/
754 : /********************************************************************/
755 : /* 19 digits (if 64 bits, at most 2^60-1) + 1 sign */
756 : static const long MAX_EXPO_LEN = 20;
757 :
758 : /* write z to buf, inserting '.' at 'point', 0 < point < strlen(z) */
759 : static void
760 160464 : wr_dec(char *buf, char *z, long point)
761 : {
762 160464 : char *s = buf + point;
763 160464 : strncpy(buf, z, point); /* integer part */
764 160464 : *s++ = '.'; z += point;
765 1281664 : while ( (*s++ = *z++) ) /* empty */;
766 160464 : }
767 :
768 : static char *
769 126 : zerotostr(void)
770 : {
771 126 : char *s = (char*)new_chunk(1);
772 126 : s[0] = '0';
773 126 : s[1] = 0; return s;
774 : }
775 :
776 : /* write a real 0 of exponent ex in format f */
777 : static char *
778 661 : real0tostr_width_frac(long width_frac)
779 : {
780 : char *buf, *s;
781 661 : if (width_frac == 0) return zerotostr();
782 654 : buf = s = stack_malloc(width_frac + 3);
783 654 : *s++ = '0';
784 654 : *s++ = '.';
785 654 : (void)zeros(s, width_frac);
786 654 : return buf;
787 : }
788 :
789 : /* write a real 0 of exponent ex */
790 : static char *
791 1756 : real0tostr(long ex, char format, char exp_char, long wanted_dec)
792 : {
793 : char *buf, *buf0;
794 :
795 1756 : if (format == 'f') {
796 0 : long width_frac = wanted_dec;
797 0 : if (width_frac < 0) width_frac = (ex >= 0)? 0: (long)(-ex * LOG10_2);
798 0 : return real0tostr_width_frac(width_frac);
799 : } else {
800 1756 : buf0 = buf = stack_malloc(3 + MAX_EXPO_LEN + 1);
801 1756 : *buf++ = '0';
802 1756 : *buf++ = '.';
803 1756 : *buf++ = exp_char;
804 1756 : sprintf(buf, "%ld", ex10(ex) + 1);
805 : }
806 1756 : return buf0;
807 : }
808 :
809 : /* format f, width_frac >= 0: number of digits in fractional part, */
810 : static char *
811 142743 : absrtostr_width_frac(GEN x, int width_frac)
812 : {
813 142743 : long beta, ls, point, lx, sx = signe(x);
814 : char *s, *buf;
815 : GEN z;
816 :
817 142743 : if (!sx) return real0tostr_width_frac(width_frac);
818 :
819 : /* x != 0 */
820 142126 : lx = realprec(x);
821 142126 : beta = width_frac;
822 142126 : if (beta) /* >= 0 */
823 : { /* z = |x| 10^beta, 10^b = 5^b * 2^b, 2^b goes into exponent */
824 123988 : if (beta > 4e9) lx++;
825 142126 : z = mulrr(x, rpowuu(5UL, (ulong)beta, lx+1));
826 142126 : setsigne(z, 1);
827 142126 : shiftr_inplace(z, beta);
828 : }
829 : else
830 0 : z = mpabs(x);
831 142126 : z = roundr_safe(z);
832 142126 : if (!signe(z)) return real0tostr_width_frac(width_frac);
833 :
834 142082 : s = itostr_sign(z, 1, &ls); /* ls > 0, number of digits in s */
835 142082 : point = ls - beta; /* position of . in s; <= ls, may be < 0 */
836 142082 : if (point > 0) /* write integer_part.fractional_part */
837 : {
838 : /* '.', trailing \0 */
839 141582 : buf = stack_malloc( ls + 1+1 );
840 141582 : if (ls == point)
841 0 : strcpy(buf, s); /* no '.' */
842 : else
843 141582 : wr_dec(buf, s, point);
844 : } else { /* point <= 0, fractional part must be written */
845 : char *t;
846 : /* '0', '.', zeroes, trailing \0 */
847 500 : buf = t = stack_malloc( 1 + 1 - point + ls + 1 );
848 500 : *t++ = '0';
849 500 : *t++ = '.';
850 500 : t = zeros(t, -point);
851 500 : strcpy(t, s);
852 : }
853 142082 : return buf;
854 : }
855 :
856 : /* Return t_REAL |x| in floating point format.
857 : * Allocate freely, the caller must clean the stack.
858 : * FORMAT: E/e (exponential), F/f (floating point), G/g
859 : * wanted_dec: number of significant digits to print (all if < 0).
860 : */
861 : static char *
862 35021 : absrtostr(GEN x, int sp, char FORMAT, long wanted_dec)
863 : {
864 35021 : const char format = (char)tolower((unsigned char)FORMAT), exp_char = (format == FORMAT)? 'e': 'E';
865 35021 : long beta, ls, point, lx, sx = signe(x), ex = expo(x);
866 : char *s, *buf, *buf0;
867 : GEN z;
868 :
869 35021 : if (!sx) return real0tostr(ex, format, exp_char, wanted_dec);
870 :
871 : /* x != 0 */
872 33265 : lx = realprec(x);
873 33265 : if (wanted_dec >= 0)
874 : { /* reduce precision if possible */
875 33265 : long w = ndec2prec(wanted_dec); /* digits -> pari precision in words */
876 33265 : if (lx > w) lx = w; /* truncature with guard, no rounding */
877 : }
878 33265 : beta = ex10(lx - ex);
879 33265 : if (beta)
880 : { /* z = |x| 10^beta, 10^b = 5^b * 2^b, 2^b goes into exponent */
881 33251 : if (beta > 0)
882 : {
883 30823 : if (beta > 18) { lx++; x = rtor(x, lx); }
884 30823 : z = mulrr(x, rpowuu(5UL, (ulong)beta, lx+1));
885 : }
886 : else
887 : {
888 2428 : if (beta < -18) { lx++; x = rtor(x, lx); }
889 2428 : z = divrr(x, rpowuu(5UL, (ulong)-beta, lx+1));
890 : }
891 33251 : setsigne(z, 1);
892 33251 : shiftr_inplace(z, beta);
893 : }
894 : else
895 14 : z = x;
896 33265 : z = roundr_safe(z);
897 33265 : if (!signe(z)) return real0tostr(ex, format, exp_char, wanted_dec);
898 :
899 33265 : s = itostr_sign(z, 1, &ls); /* ls > 0, number of digits in s */
900 33265 : if (wanted_dec < 0)
901 0 : wanted_dec = ls;
902 33265 : else if (ls > wanted_dec)
903 : {
904 25026 : beta -= ls - wanted_dec;
905 25026 : ls = wanted_dec;
906 25026 : if (s[ls] >= '5') /* round up */
907 : {
908 : long i;
909 17997 : for (i = ls-1; i >= 0; s[i--] = '0')
910 17990 : if (++s[i] <= '9') break;
911 11617 : if (i < 0) { s[0] = '1'; beta--; }
912 : }
913 25026 : s[ls] = 0;
914 : }
915 :
916 : /* '.', " E", exponent, trailing \0 */
917 33265 : point = ls - beta; /* position of . in s; < 0 or > 0 */
918 33265 : if (beta <= 0 || format == 'e' || (format == 'g' && point-1 < -4))
919 : { /* e format */
920 4163 : buf0 = buf = stack_malloc(ls+1+2+MAX_EXPO_LEN + 1);
921 4163 : wr_dec(buf, s, 1); buf += ls + 1;
922 4163 : if (sp) *buf++ = ' ';
923 4163 : *buf++ = exp_char;
924 4163 : sprintf(buf, "%ld", point-1);
925 : }
926 29102 : else if (point > 0) /* f format, write integer_part.fractional_part */
927 : {
928 14719 : buf0 = buf = stack_malloc(ls+1 + 1);
929 14719 : wr_dec(buf, s, point); /* point < ls since beta > 0 */
930 : }
931 : else /* f format, point <= 0, write fractional part */
932 : {
933 14383 : buf0 = buf = stack_malloc(2-point+ls + 1);
934 14383 : *buf++ = '0';
935 14383 : *buf++ = '.';
936 14383 : buf = zeros(buf, -point);
937 14383 : strcpy(buf, s);
938 : }
939 33265 : return buf0;
940 : }
941 :
942 : /* vsnprintf implementation rewritten from snprintf.c to be found at
943 : *
944 : * http://www.nersc.gov/~scottc/misc/docs/snort-2.1.1-RC1/snprintf_8c-source.html
945 : * The original code was
946 : * Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
947 : * available under the terms of the GNU GPL version 2 or later. It
948 : * was itself adapted from an original version by Patrick Powell. */
949 :
950 : /* Modifications for format %Ps: R.Butel IMB/CNRS 2007/12/03 */
951 :
952 : /* l = old len, L = new len */
953 : static void
954 2128 : str_alloc0(pari_str *S, long l, long L)
955 : {
956 2128 : if (S->use_stack)
957 2100 : S->string = (char*) memcpy(stack_malloc(L), S->string, l);
958 : else
959 28 : pari_realloc_ip((void**)&S->string, L);
960 2128 : S->cur = S->string + l;
961 2128 : S->end = S->string + L;
962 2128 : S->size = L;
963 2128 : }
964 : /* make sure S is large enough to write l further words (<= l * 20 chars).
965 : * To avoid automatic extension in between av = avma / set_avma(av) pairs
966 : * [ would destroy S->string if (S->use_stack) ] */
967 : static void
968 629552 : str_alloc(pari_str *S, long l)
969 : {
970 629552 : l *= 20;
971 629552 : if (S->end - S->cur <= l)
972 1397 : str_alloc0(S, S->cur - S->string, S->size + maxss(S->size, l));
973 629552 : }
974 : void
975 15656773 : str_putc(pari_str *S, char c)
976 : {
977 15656773 : *S->cur++ = c;
978 15656773 : if (S->cur == S->end) str_alloc0(S, S->size, S->size << 1);
979 15656773 : }
980 :
981 : void
982 305965 : str_init(pari_str *S, int use_stack)
983 : {
984 : char *s;
985 305965 : S->size = 1024;
986 305965 : S->use_stack = use_stack;
987 305965 : if (S->use_stack)
988 213988 : s = (char*)stack_malloc(S->size);
989 : else
990 91977 : s = (char*)pari_malloc(S->size);
991 305965 : *s = 0;
992 305965 : S->string = S->cur = s;
993 305965 : S->end = S->string + S->size;
994 305965 : }
995 : void
996 14462482 : str_puts(pari_str *S, const char *s) { while (*s) str_putc(S, *s++); }
997 :
998 : static void
999 159381 : str_putscut(pari_str *S, const char *s, int cut)
1000 : {
1001 159381 : if (cut < 0) str_puts(S, s);
1002 : else {
1003 140 : while (*s && cut-- > 0) str_putc(S, *s++);
1004 : }
1005 159381 : }
1006 :
1007 : /* lbuf = strlen(buf), len < 0: unset */
1008 : static void
1009 289241 : outpad(pari_str *S, const char *buf, long lbuf, int sign, long ljust, long len, long zpad)
1010 : {
1011 289241 : long padlen = len - lbuf;
1012 289241 : if (padlen < 0) padlen = 0;
1013 289241 : if (ljust) padlen = -padlen;
1014 289241 : if (padlen > 0)
1015 : {
1016 357 : if (zpad) {
1017 56 : if (sign) { str_putc(S, sign); --padlen; }
1018 252 : while (padlen > 0) { str_putc(S, '0'); --padlen; }
1019 : }
1020 : else
1021 : {
1022 301 : if (sign) --padlen;
1023 1106 : while (padlen > 0) { str_putc(S, ' '); --padlen; }
1024 301 : if (sign) str_putc(S, sign);
1025 : }
1026 : } else
1027 288884 : if (sign) str_putc(S, sign);
1028 289241 : str_puts(S, buf);
1029 289598 : while (padlen < 0) { str_putc(S, ' '); ++padlen; }
1030 289241 : }
1031 :
1032 : /* len < 0 or maxwidth < 0: unset */
1033 : static void
1034 159381 : fmtstr(pari_str *S, const char *buf, int ljust, int len, int maxwidth)
1035 : {
1036 159381 : int padlen, lbuf = strlen(buf);
1037 :
1038 159381 : if (maxwidth >= 0 && lbuf > maxwidth) lbuf = maxwidth;
1039 :
1040 159381 : padlen = len - lbuf;
1041 159381 : if (padlen < 0) padlen = 0;
1042 159381 : if (ljust) padlen = -padlen;
1043 159500 : while (padlen > 0) { str_putc(S, ' '); --padlen; }
1044 159381 : str_putscut(S, buf, maxwidth);
1045 159381 : while (padlen < 0) { str_putc(S, ' '); ++padlen; }
1046 159381 : }
1047 :
1048 : /* abs(base) is 8, 10, 16. If base < 0, some "alternate" form
1049 : * -- print hex in uppercase
1050 : * -- prefix octal with 0
1051 : * signvalue = -1: unsigned, otherwise ' ' or '+'. Leaves a messy stack if
1052 : * S->use_stack */
1053 : static void
1054 146267 : fmtnum(pari_str *S, long lvalue, GEN gvalue, int base, int signvalue,
1055 : int ljust, int len, int zpad)
1056 : {
1057 : int caps;
1058 : char *buf0, *buf;
1059 : long lbuf, mxl;
1060 146267 : GEN uvalue = NULL;
1061 146267 : ulong ulvalue = 0;
1062 146267 : pari_sp av = avma;
1063 :
1064 146267 : if (gvalue)
1065 : {
1066 : long s, l;
1067 2254 : if (typ(gvalue) != t_INT) {
1068 : long i, j, h;
1069 70 : l = lg(gvalue);
1070 70 : switch(typ(gvalue))
1071 : {
1072 56 : case t_COMPLEX:
1073 56 : fmtnum(S, 0, gel(gvalue,1), base, signvalue, ljust,len,zpad);
1074 56 : if (gsigne(gel(gvalue,2)) >= 0) str_putc(S, '+');
1075 56 : fmtnum(S, 0, gel(gvalue,2), base, signvalue, ljust,len,zpad);
1076 56 : str_putc(S, '*');
1077 56 : str_putc(S, 'I');
1078 56 : return;
1079 0 : case t_VEC:
1080 0 : str_putc(S, '[');
1081 0 : for (i = 1; i < l; i++)
1082 : {
1083 0 : fmtnum(S, 0, gel(gvalue,i), base, signvalue, ljust,len,zpad);
1084 0 : if (i < l-1) str_putc(S, ',');
1085 : }
1086 0 : str_putc(S, ']');
1087 0 : return;
1088 0 : case t_COL:
1089 0 : str_putc(S, '[');
1090 0 : for (i = 1; i < l; i++)
1091 : {
1092 0 : fmtnum(S, 0, gel(gvalue,i), base, signvalue, ljust,len,zpad);
1093 0 : if (i < l-1) str_putc(S, ',');
1094 : }
1095 0 : str_putc(S, ']');
1096 0 : str_putc(S, '~');
1097 0 : return;
1098 14 : case t_MAT:
1099 14 : if (l == 1)
1100 0 : str_puts(S, "[;]");
1101 : else
1102 : {
1103 14 : h = lgcols(gvalue);
1104 63 : for (i=1; i<h; i++)
1105 : {
1106 49 : str_putc(S, '[');
1107 168 : for (j=1; j<l; j++)
1108 : {
1109 119 : fmtnum(S, 0, gcoeff(gvalue,i,j), base, signvalue, ljust,len,zpad);
1110 119 : if (j<l-1) str_putc(S, ' ');
1111 : }
1112 49 : str_putc(S, ']');
1113 49 : str_putc(S, '\n');
1114 49 : if (i<h-1) str_putc(S, '\n');
1115 : }
1116 : }
1117 14 : return;
1118 : }
1119 0 : gvalue = gfloor( simplify_shallow(gvalue) );
1120 0 : if (typ(gvalue) != t_INT)
1121 0 : pari_err(e_MISC,"not a t_INT in integer format conversion: %Ps", gvalue);
1122 : }
1123 2184 : s = signe(gvalue);
1124 2184 : if (!s) { lbuf = 1; buf = zerotostr(); signvalue = 0; goto END; }
1125 :
1126 2065 : l = lgefint(gvalue);
1127 2065 : uvalue = gvalue;
1128 2065 : if (signvalue < 0)
1129 : {
1130 651 : if (s < 0) uvalue = addii(int2n(bit_accuracy(l)), gvalue);
1131 651 : signvalue = 0;
1132 : }
1133 : else
1134 : {
1135 1414 : if (s < 0) { signvalue = '-'; uvalue = absi(uvalue); }
1136 : }
1137 2065 : mxl = (l-2)* 22 + 1; /* octal at worst; 22 octal chars per 64bit word */
1138 : } else {
1139 144013 : ulvalue = lvalue;
1140 144013 : if (signvalue < 0)
1141 756 : signvalue = 0;
1142 : else
1143 143257 : if (lvalue < 0) { signvalue = '-'; ulvalue = - lvalue; }
1144 144013 : mxl = 22 + 1; /* octal at worst; 22 octal chars to write down 2^64 - 1 */
1145 : }
1146 146078 : if (base > 0) caps = 0; else { caps = 1; base = -base; }
1147 :
1148 146078 : buf0 = buf = stack_malloc(mxl) + mxl; /* fill from the right */
1149 146078 : *--buf = 0; /* trailing \0 */
1150 146078 : if (gvalue) {
1151 2065 : if (base == 10) {
1152 : long i, l, cnt;
1153 1414 : ulong *larray = convi(uvalue, &l);
1154 1414 : larray -= l;
1155 10073 : for (i = 0; i < l; i++) {
1156 8659 : cnt = 0;
1157 8659 : ulvalue = larray[i];
1158 : do {
1159 66262 : *--buf = '0' + ulvalue%10;
1160 66262 : ulvalue = ulvalue / 10;
1161 66262 : cnt++;
1162 66262 : } while (ulvalue);
1163 8659 : if (i + 1 < l)
1164 8372 : for (;cnt<9;cnt++) *--buf = '0';
1165 : }
1166 651 : } else if (base == 16) {
1167 651 : long i, l = lgefint(uvalue);
1168 651 : GEN up = int_LSW(uvalue);
1169 2963 : for (i = 2; i < l; i++, up = int_nextW(up)) {
1170 2312 : ulong ucp = (ulong)*up;
1171 : long j;
1172 29703 : for (j=0; j < BITS_IN_LONG/4; j++) {
1173 28042 : unsigned char cv = ucp & 0xF;
1174 28042 : *--buf = (caps? "0123456789ABCDEF":"0123456789abcdef")[cv];
1175 28042 : ucp >>= 4;
1176 28042 : if (ucp == 0 && i+1 == l) break;
1177 : }
1178 : } /* loop on hex digits in word */
1179 0 : } else if (base == 8) {
1180 0 : long i, l = lgefint(uvalue);
1181 0 : GEN up = int_LSW(uvalue);
1182 0 : ulong rem = 0;
1183 0 : int shift = 0;
1184 0 : int mask[3] = {0, 1, 3};
1185 0 : for (i = 2; i < l; i++, up = int_nextW(up)) {
1186 0 : ulong ucp = (ulong)*up;
1187 0 : long j, ldispo = BITS_IN_LONG;
1188 0 : if (shift) { /* 0, 1 or 2 */
1189 0 : unsigned char cv = ((ucp & mask[shift]) <<(3-shift)) + rem;
1190 0 : *--buf = "01234567"[cv];
1191 0 : ucp >>= shift;
1192 0 : ldispo -= shift;
1193 : };
1194 0 : shift = (shift + 3 - BITS_IN_LONG % 3) % 3;
1195 0 : for (j=0; j < BITS_IN_LONG/3; j++) {
1196 0 : unsigned char cv = ucp & 0x7;
1197 0 : if (ucp == 0 && i+1 == l) { rem = 0; break; };
1198 0 : *--buf = "01234567"[cv];
1199 0 : ucp >>= 3;
1200 0 : ldispo -= 3;
1201 0 : rem = ucp;
1202 0 : if (ldispo < 3) break;
1203 : }
1204 : } /* loop on hex digits in word */
1205 0 : if (rem) *--buf = "01234567"[rem];
1206 : }
1207 : } else { /* not a gvalue, thus a standard integer */
1208 : do {
1209 357500 : *--buf = (caps? "0123456789ABCDEF":"0123456789abcdef")[ulvalue % (unsigned)base ];
1210 357500 : ulvalue /= (unsigned)base;
1211 357500 : } while (ulvalue);
1212 : }
1213 : /* leading 0 if octal and alternate # form */
1214 146078 : if (caps && base == 8) *--buf = '0';
1215 146078 : lbuf = (buf0 - buf) - 1;
1216 146197 : END:
1217 146197 : outpad(S, buf, lbuf, signvalue, ljust, len, zpad);
1218 146197 : if (!S->use_stack) set_avma(av);
1219 : }
1220 :
1221 : static GEN
1222 1876 : v_get_arg(pari_str *S, GEN arg_vector, int *index, const char *save_fmt)
1223 : {
1224 1876 : if (*index >= lg(arg_vector))
1225 : {
1226 7 : if (!S->use_stack) pari_free(S->string);
1227 7 : pari_err(e_MISC, "missing arg %d for printf format '%s'", *index, save_fmt); }
1228 1869 : return gel(arg_vector, (*index)++);
1229 : }
1230 :
1231 : static int
1232 287421 : dosign(int blank, int plus)
1233 : {
1234 287421 : if (plus) return('+');
1235 287407 : if (blank) return(' ');
1236 287407 : return 0;
1237 : }
1238 :
1239 : /* x * 10 + 'digit whose char value is ch'. Do not check for overflow */
1240 : static int
1241 143485 : shift_add(int x, int ch)
1242 : {
1243 143485 : if (x < 0) /* was unset */
1244 143282 : x = ch - '0';
1245 : else
1246 203 : x = x*10 + ch - '0';
1247 143485 : return x;
1248 : }
1249 :
1250 : static long
1251 143044 : get_sigd(GEN gvalue, char ch, int maxwidth)
1252 : {
1253 : long e;
1254 143044 : if (maxwidth < 0) return nbits2ndec(precreal);
1255 143030 : switch(ch)
1256 : {
1257 147 : case 'E': case 'e': return maxwidth+1;
1258 142743 : case 'F': case 'f':
1259 142743 : e = gexpo(gvalue);
1260 142743 : if (e == -(long)HIGHEXPOBIT) return 0;
1261 142708 : e = ex10(e); if (e < 0) e = 0;
1262 142708 : return e + 1 + maxwidth;
1263 : }
1264 140 : return maxwidth? maxwidth: 1; /* 'g', 'G' */
1265 : }
1266 :
1267 : static void
1268 143128 : fmtreal(pari_str *S, GEN gvalue, int space, int signvalue, int FORMAT,
1269 : int maxwidth, int ljust, int len, int zpad)
1270 : {
1271 143128 : pari_sp av = avma;
1272 : long sigd;
1273 : char *buf;
1274 :
1275 143128 : if (typ(gvalue) == t_REAL)
1276 142841 : sigd = get_sigd(gvalue, FORMAT, maxwidth);
1277 : else
1278 : {
1279 287 : long i, j, h, l = lg(gvalue);
1280 287 : switch(typ(gvalue))
1281 : {
1282 42 : case t_COMPLEX:
1283 42 : fmtreal(S, gel(gvalue,1), space, signvalue, FORMAT, maxwidth,
1284 : ljust,len,zpad);
1285 42 : if (gsigne(gel(gvalue,2)) >= 0) str_putc(S, '+');
1286 42 : fmtreal(S, gel(gvalue,2), space, signvalue, FORMAT, maxwidth,
1287 : ljust,len,zpad);
1288 42 : str_putc(S, 'I');
1289 42 : return;
1290 :
1291 28 : case t_VEC:
1292 28 : str_putc(S, '[');
1293 84 : for (i = 1; i < l; i++)
1294 : {
1295 56 : fmtreal(S, gel(gvalue,i), space, signvalue, FORMAT, maxwidth,
1296 : ljust,len,zpad);
1297 56 : if (i < l-1) str_putc(S, ',');
1298 : }
1299 28 : str_putc(S, ']');
1300 28 : return;
1301 0 : case t_COL:
1302 0 : str_putc(S, '[');
1303 0 : for (i = 1; i < l; i++)
1304 : {
1305 0 : fmtreal(S, gel(gvalue,i), space, signvalue, FORMAT, maxwidth,
1306 : ljust,len,zpad);
1307 0 : if (i < l-1) str_putc(S, ',');
1308 : }
1309 0 : str_putc(S, ']');
1310 0 : str_putc(S, '~');
1311 0 : return;
1312 14 : case t_MAT:
1313 14 : if (l == 1)
1314 0 : str_puts(S, "[;]");
1315 : else
1316 : {
1317 14 : h = lgcols(gvalue);
1318 42 : for (j=1; j<h; j++)
1319 : {
1320 28 : str_putc(S, '[');
1321 105 : for (i=1; i<l; i++)
1322 : {
1323 77 : fmtreal(S, gcoeff(gvalue,j,i), space, signvalue, FORMAT, maxwidth,
1324 : ljust,len,zpad);
1325 77 : if (i<l-1) str_putc(S, ' ');
1326 : }
1327 28 : str_putc(S, ']');
1328 28 : str_putc(S, '\n');
1329 28 : if (j<h-1) str_putc(S, '\n');
1330 : }
1331 : }
1332 14 : return;
1333 : }
1334 203 : sigd = get_sigd(gvalue, FORMAT, maxwidth);
1335 203 : gvalue = gtofp(gvalue, maxss(ndec2prec(sigd), LOWDEFAULTPREC));
1336 203 : if (typ(gvalue) != t_REAL)
1337 : {
1338 0 : if (!S->use_stack) free(S->string);
1339 0 : pari_err(e_MISC,"impossible conversion to t_REAL: %Ps",gvalue);
1340 : }
1341 : }
1342 143044 : if ((FORMAT == 'f' || FORMAT == 'F') && maxwidth >= 0)
1343 142743 : buf = absrtostr_width_frac(gvalue, maxwidth);
1344 : else
1345 301 : buf = absrtostr(gvalue, space, FORMAT, sigd);
1346 143044 : if (signe(gvalue) < 0) signvalue = '-';
1347 143044 : outpad(S, buf, strlen(buf), signvalue, ljust, len, zpad);
1348 143044 : if (!S->use_stack) set_avma(av);
1349 : }
1350 : static long
1351 77 : gtolong_OK(GEN x)
1352 : {
1353 77 : switch(typ(x))
1354 : {
1355 56 : case t_INT: case t_REAL: case t_FRAC: return 1;
1356 7 : case t_COMPLEX: return gequal0(gel(x,2)) && gtolong_OK(gel(x,1));
1357 7 : case t_QUAD: return gequal0(gel(x,3)) && gtolong_OK(gel(x,2));
1358 : }
1359 7 : return 0;
1360 : }
1361 : /* Format handling "inspired" by the standard draft at
1362 : -- http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf pages 274ff
1363 : * fmt is a standard printf format, except 'P' is a "length modifier"
1364 : * allowing GEN arguments. Use either the arg_vector or (if NULL) the va_list.
1365 : * Appent output to the pari_str S, which must be initialized; clean if
1366 : * !S->use_stack, else leaves objects of stack. */
1367 : static void
1368 228124 : str_arg_vprintf(pari_str *S, const char *fmt, GEN arg_vector, va_list args)
1369 : {
1370 228124 : int GENflag = 0, longflag = 0, pointflag = 0;
1371 : int print_plus, print_blank, with_sharp, ch, ljust, len, maxwidth, zpad;
1372 : long lvalue;
1373 228124 : int index = 1;
1374 : GEN gvalue;
1375 228124 : const char *save_fmt = fmt;
1376 :
1377 2342098 : while ((ch = *fmt++) != '\0') {
1378 2113995 : switch(ch) {
1379 448762 : case '%':
1380 448762 : ljust = zpad = 0;
1381 448762 : len = maxwidth = -1;
1382 448762 : GENflag = longflag = pointflag = 0;
1383 448762 : print_plus = print_blank = with_sharp = 0;
1384 901683 : nextch:
1385 901683 : ch = *fmt++;
1386 901683 : switch(ch) {
1387 0 : case 0:
1388 0 : pari_err(e_MISC, "printf: end of format");
1389 : /*------------------------------------------------------------------------
1390 : -- flags
1391 : ------------------------------------------------------------------------*/
1392 42 : case '-':
1393 42 : ljust = 1;
1394 42 : goto nextch;
1395 14 : case '+':
1396 14 : print_plus = 1;
1397 14 : goto nextch;
1398 14 : case '#':
1399 14 : with_sharp = 1;
1400 14 : goto nextch;
1401 0 : case ' ':
1402 0 : print_blank = 1;
1403 0 : goto nextch;
1404 952 : case '0':
1405 : /* appears as a flag: set zero padding */
1406 952 : if (len < 0 && !pointflag) { zpad = '0'; goto nextch; }
1407 :
1408 : /* else part of a field width or precision */
1409 : /* fall through */
1410 : /*------------------------------------------------------------------------
1411 : -- maxwidth or precision
1412 : ------------------------------------------------------------------------*/
1413 : case '1':
1414 : case '2':
1415 : case '3':
1416 : case '4':
1417 : case '5':
1418 : case '6':
1419 : case '7':
1420 : case '8':
1421 : case '9':
1422 143485 : if (pointflag)
1423 143037 : maxwidth = shift_add(maxwidth, ch);
1424 : else
1425 448 : len = shift_add(len, ch);
1426 143485 : goto nextch;
1427 :
1428 28 : case '*':
1429 : {
1430 28 : int *t = pointflag? &maxwidth: &len;
1431 28 : if (arg_vector)
1432 : {
1433 28 : gvalue = v_get_arg(S, arg_vector, &index, save_fmt);
1434 28 : if (!gtolong_OK(gvalue) && !S->use_stack) pari_free(S->string);
1435 28 : *t = (int)gtolong(gvalue);
1436 : }
1437 : else
1438 0 : *t = va_arg(args, int);
1439 28 : goto nextch;
1440 : }
1441 142939 : case '.':
1442 142939 : if (pointflag)
1443 0 : pari_err(e_MISC, "two '.' in conversion specification");
1444 142939 : pointflag = 1;
1445 142939 : goto nextch;
1446 : /*------------------------------------------------------------------------
1447 : -- length modifiers
1448 : ------------------------------------------------------------------------*/
1449 144900 : case 'l':
1450 144900 : if (GENflag)
1451 0 : pari_err(e_MISC, "P/l length modifiers in the same conversion");
1452 : #if !defined(_WIN64)
1453 144900 : if (longflag)
1454 0 : pari_err_IMPL( "ll length modifier in printf");
1455 : #endif
1456 144900 : longflag = 1;
1457 144900 : goto nextch;
1458 20729 : case 'P':
1459 20729 : if (longflag)
1460 0 : pari_err(e_MISC, "P/l length modifiers in the same conversion");
1461 20729 : if (GENflag)
1462 0 : pari_err(e_MISC, "'P' length modifier appears twice");
1463 20729 : GENflag = 1;
1464 20729 : goto nextch;
1465 0 : case 'h': /* dummy: va_arg promotes short into int */
1466 0 : goto nextch;
1467 : /*------------------------------------------------------------------------
1468 : -- conversions
1469 : ------------------------------------------------------------------------*/
1470 756 : case 'u': /* not a signed conversion: print_(blank|plus) ignored */
1471 : #define get_num_arg() \
1472 : if (arg_vector) { \
1473 : lvalue = 0; \
1474 : gvalue = v_get_arg(S, arg_vector, &index, save_fmt); \
1475 : } else { \
1476 : if (GENflag) { \
1477 : lvalue = 0; \
1478 : gvalue = va_arg(args, GEN); \
1479 : } else { \
1480 : lvalue = longflag? va_arg(args, long): va_arg(args, int); \
1481 : gvalue = NULL; \
1482 : } \
1483 : }
1484 756 : get_num_arg();
1485 756 : fmtnum(S, lvalue, gvalue, 10, -1, ljust, len, zpad);
1486 756 : break;
1487 0 : case 'o': /* not a signed conversion: print_(blank|plus) ignored */
1488 0 : get_num_arg();
1489 0 : fmtnum(S, lvalue, gvalue, with_sharp? -8: 8, -1, ljust, len, zpad);
1490 0 : break;
1491 144517 : case 'd':
1492 : case 'i':
1493 144517 : get_num_arg();
1494 144510 : fmtnum(S, lvalue, gvalue, 10,
1495 : dosign(print_blank, print_plus), ljust, len, zpad);
1496 144510 : break;
1497 0 : case 'p':
1498 0 : str_putc(S, '0'); str_putc(S, 'x');
1499 0 : if (arg_vector)
1500 0 : lvalue = (long)v_get_arg(S, arg_vector, &index, save_fmt);
1501 : else
1502 0 : lvalue = (long)va_arg(args, void*);
1503 0 : fmtnum(S, lvalue, NULL, 16, -1, ljust, len, zpad);
1504 0 : break;
1505 14 : case 'x': /* not a signed conversion: print_(blank|plus) ignored */
1506 14 : if (with_sharp) { str_putc(S, '0'); str_putc(S, 'x'); }
1507 14 : get_num_arg();
1508 14 : fmtnum(S, lvalue, gvalue, 16, -1, ljust, len, zpad);
1509 14 : break;
1510 756 : case 'X': /* not a signed conversion: print_(blank|plus) ignored */
1511 756 : if (with_sharp) { str_putc(S, '0'); str_putc(S, 'X'); }
1512 756 : get_num_arg();
1513 756 : fmtnum(S, lvalue, gvalue,-16, -1, ljust, len, zpad);
1514 756 : break;
1515 159381 : case 's':
1516 : {
1517 : char *strvalue;
1518 159381 : pari_sp av = avma;
1519 :
1520 159381 : if (arg_vector) {
1521 126 : gvalue = v_get_arg(S, arg_vector, &index, save_fmt);
1522 126 : strvalue = NULL;
1523 : } else {
1524 159255 : if (GENflag) {
1525 19980 : gvalue = va_arg(args, GEN);
1526 19980 : strvalue = NULL;
1527 : } else {
1528 139275 : gvalue = NULL;
1529 139275 : strvalue = va_arg(args, char *);
1530 : }
1531 : }
1532 159381 : if (gvalue) strvalue = GENtostr_unquoted(gvalue);
1533 159381 : fmtstr(S, strvalue, ljust, len, maxwidth);
1534 159381 : if (!S->use_stack) set_avma(av);
1535 159381 : break;
1536 : }
1537 42 : case 'c':
1538 42 : gvalue = NULL;
1539 42 : if (arg_vector)
1540 35 : gvalue = v_get_arg(S, arg_vector, &index, save_fmt);
1541 7 : else if (GENflag)
1542 0 : gvalue = va_arg(args,GEN);
1543 : else
1544 : {
1545 7 : ch = va_arg(args, int);
1546 7 : str_putc(S, ch); break;
1547 : }
1548 35 : if (!gtolong_OK(gvalue) && !S->use_stack) free(S->string);
1549 35 : str_putc(S, (int)gtolong(gvalue));
1550 28 : break;
1551 :
1552 378 : case '%':
1553 378 : str_putc(S, ch);
1554 378 : continue;
1555 142911 : case 'g':
1556 : case 'G':
1557 : case 'e':
1558 : case 'E':
1559 : case 'f':
1560 : case 'F':
1561 : {
1562 142911 : pari_sp av = avma;
1563 142911 : if (arg_vector)
1564 392 : gvalue = simplify_shallow(v_get_arg(S, arg_vector, &index, save_fmt));
1565 : else {
1566 142519 : if (GENflag)
1567 0 : gvalue = simplify_shallow( va_arg(args, GEN) );
1568 : else
1569 142519 : gvalue = dbltor( va_arg(args, double) );
1570 : }
1571 142911 : fmtreal(S, gvalue, GP_DATA->fmt->sp, dosign(print_blank,print_plus),
1572 : ch, maxwidth, ljust, len, zpad);
1573 142911 : if (!S->use_stack) set_avma(av);
1574 142911 : break;
1575 : }
1576 7 : default:
1577 7 : if (!S->use_stack) free(S->string);
1578 7 : pari_err(e_MISC, "invalid conversion or specification %c in format `%s'", ch, save_fmt);
1579 : } /* second switch on ch */
1580 448363 : break;
1581 1665233 : default:
1582 1665233 : str_putc(S, ch);
1583 1665233 : break;
1584 : } /* first switch on ch */
1585 : } /* while loop on ch */
1586 228103 : *S->cur = 0;
1587 228103 : }
1588 :
1589 : void
1590 12 : decode_color(long n, long *c)
1591 : {
1592 12 : c[1] = n & 0xf; n >>= 4; /* foreground */
1593 12 : c[2] = n & 0xf; n >>= 4; /* background */
1594 12 : c[0] = n & 0xf; /* attribute */
1595 12 : }
1596 :
1597 : #define COLOR_LEN 16
1598 : /* start printing in "color" c */
1599 : /* terminal has to support ANSI color escape sequences */
1600 : void
1601 68398 : out_term_color(PariOUT *out, long c)
1602 : {
1603 : static char s[COLOR_LEN];
1604 68398 : out->puts(term_get_color(s, c));
1605 68398 : }
1606 : void
1607 693 : term_color(long c) { out_term_color(pariOut, c); }
1608 :
1609 : /* s must be able to store 12 chars (including final \0) */
1610 : char *
1611 83206 : term_get_color(char *s, long n)
1612 : {
1613 : long c[3], a;
1614 83206 : if (!s) s = stack_malloc(COLOR_LEN);
1615 :
1616 83206 : if (disable_color) { *s = 0; return s; }
1617 16 : if (n == c_NONE || (a = gp_colors[n]) == c_NONE)
1618 4 : strcpy(s, "\x1b[0m"); /* reset */
1619 : else
1620 : {
1621 12 : decode_color(a,c);
1622 12 : if (c[1]<8) c[1] += 30; else c[1] += 82;
1623 12 : if (a & (1L<<12)) /* transparent background */
1624 12 : sprintf(s, "\x1b[%ld;%ldm", c[0], c[1]);
1625 : else
1626 : {
1627 0 : if (c[2]<8) c[2] += 40; else c[2] += 92;
1628 0 : sprintf(s, "\x1b[%ld;%ld;%ldm", c[0], c[1], c[2]);
1629 : }
1630 : }
1631 16 : return s;
1632 : }
1633 :
1634 : static long
1635 173691 : strlen_real(const char *s)
1636 : {
1637 173691 : const char *t = s;
1638 173691 : long len = 0;
1639 1310352 : while (*t)
1640 : {
1641 1136661 : if (t[0] == '\x1b' && t[1] == '[')
1642 : { /* skip ANSI escape sequence */
1643 2 : t += 2;
1644 10 : while (*t && *t++ != 'm') /* empty */;
1645 2 : continue;
1646 : }
1647 1136659 : t++; len++;
1648 : }
1649 173691 : return len;
1650 : }
1651 :
1652 : #undef COLOR_LEN
1653 :
1654 : /********************************************************************/
1655 : /** **/
1656 : /** PRINTING BASED ON SCREEN WIDTH **/
1657 : /** **/
1658 : /********************************************************************/
1659 : #undef larg /* problems with SCO Unix headers (ioctl_arg) */
1660 : #ifdef HAS_TIOCGWINSZ
1661 : # ifdef __sun
1662 : # include <sys/termios.h>
1663 : # endif
1664 : # include <sys/ioctl.h>
1665 : #endif
1666 :
1667 : static int
1668 22957 : term_width_intern(void)
1669 : {
1670 : #ifdef _WIN32
1671 : return win32_terminal_width();
1672 : #endif
1673 : #ifdef HAS_TIOCGWINSZ
1674 : {
1675 : struct winsize s;
1676 22957 : if (!(GP_DATA->flags & (gpd_EMACS|gpd_TEXMACS))
1677 22957 : && !ioctl(0, TIOCGWINSZ, &s)) return s.ws_col;
1678 : }
1679 : #endif
1680 : {
1681 : char *str;
1682 22957 : if ((str = os_getenv("COLUMNS"))) return atoi(str);
1683 : }
1684 : #ifdef __EMX__
1685 : {
1686 : int scrsize[2];
1687 : _scrsize(scrsize); return scrsize[0];
1688 : }
1689 : #endif
1690 22957 : return 0;
1691 : }
1692 :
1693 : static int
1694 7 : term_height_intern(void)
1695 : {
1696 : #ifdef _WIN32
1697 : return win32_terminal_height();
1698 : #endif
1699 : #ifdef HAS_TIOCGWINSZ
1700 : {
1701 : struct winsize s;
1702 7 : if (!(GP_DATA->flags & (gpd_EMACS|gpd_TEXMACS))
1703 7 : && !ioctl(0, TIOCGWINSZ, &s)) return s.ws_row;
1704 : }
1705 : #endif
1706 : {
1707 : char *str;
1708 7 : if ((str = os_getenv("LINES"))) return atoi(str);
1709 : }
1710 : #ifdef __EMX__
1711 : {
1712 : int scrsize[2];
1713 : _scrsize(scrsize); return scrsize[1];
1714 : }
1715 : #endif
1716 7 : return 0;
1717 : }
1718 :
1719 : #define DFT_TERM_WIDTH 80
1720 : #define DFT_TERM_HEIGHT 20
1721 :
1722 : int
1723 22957 : term_width(void)
1724 : {
1725 22957 : int n = term_width_intern();
1726 22957 : return (n>1)? n: DFT_TERM_WIDTH;
1727 : }
1728 :
1729 : int
1730 7 : term_height(void)
1731 : {
1732 7 : int n = term_height_intern();
1733 7 : return (n>1)? n: DFT_TERM_HEIGHT;
1734 : }
1735 :
1736 : static ulong col_index;
1737 :
1738 : /* output string wrapped after MAX_WIDTH characters (for gp -test) */
1739 : static void
1740 10150927 : putc_lw(char c)
1741 : {
1742 10150927 : if (c == '\n') col_index = 0;
1743 9943040 : else if (col_index >= GP_DATA->linewrap) { normalOutC('\n'); col_index = 1; }
1744 9862778 : else col_index++;
1745 10150927 : normalOutC(c);
1746 10150927 : }
1747 : static void
1748 10247320 : puts_lw(const char *s) { while (*s) putc_lw(*s++); }
1749 :
1750 : static PariOUT pariOut_lw= {putc_lw, puts_lw, normalOutF};
1751 :
1752 : void
1753 62780 : init_linewrap(long w) { col_index=0; GP_DATA->linewrap=w; pariOut=&pariOut_lw; }
1754 :
1755 : static void
1756 7919 : new_line(PariOUT *out, const char *prefix)
1757 : {
1758 7919 : out_putc(out, '\n'); if (prefix) out_puts(out, prefix);
1759 7919 : }
1760 :
1761 : #define is_blank(c) ((c) == ' ' || (c) == '\n' || (c) == '\t')
1762 : /* output: <prefix>< s wrapped at EOL >
1763 : * <prefix>< ... > <str>
1764 : * ^--- (no \n at the end)
1765 : * If str is NULL, omit the arrow, end the text with '\n'.
1766 : * If prefix is NULL, use "" */
1767 : void
1768 18825 : print_prefixed_text(PariOUT *out, const char *s, const char *prefix,
1769 : const char *str)
1770 : {
1771 18825 : const long prelen = prefix? strlen_real(prefix): 0;
1772 18825 : const long W = term_width(), ls = strlen(s);
1773 18825 : long linelen = prelen;
1774 18825 : char *word = (char*)pari_malloc(ls + 3);
1775 :
1776 18825 : if (prefix) out_puts(out, prefix);
1777 : for(;;)
1778 128077 : {
1779 : long len;
1780 146902 : int blank = 0;
1781 146902 : char *u = word;
1782 930726 : while (*s && !is_blank(*s)) *u++ = *s++;
1783 146902 : *u = 0; /* finish "word" */
1784 146902 : len = strlen_real(word);
1785 146902 : linelen += len;
1786 146902 : if (linelen >= W) { new_line(out, prefix); linelen = prelen + len; }
1787 146902 : out_puts(out, word);
1788 286970 : while (is_blank(*s)) {
1789 140068 : switch (*s) {
1790 137415 : case ' ': break;
1791 0 : case '\t':
1792 0 : linelen = (linelen & ~7UL) + 8; out_putc(out, '\t');
1793 0 : blank = 1; break;
1794 2653 : case '\n':
1795 2653 : linelen = W;
1796 2653 : blank = 1; break;
1797 : }
1798 140068 : if (linelen >= W) { new_line(out, prefix); linelen = prelen; }
1799 140068 : s++;
1800 : }
1801 146902 : if (!*s) break;
1802 128077 : if (!blank) { out_putc(out, ' '); linelen++; }
1803 : }
1804 18825 : if (!str)
1805 5462 : out_putc(out, '\n');
1806 : else
1807 : {
1808 13363 : long i,len = strlen_real(str);
1809 13363 : int space = (*str == ' ' && str[1]);
1810 13363 : if (linelen + len >= W)
1811 : {
1812 21 : new_line(out, prefix); linelen = prelen;
1813 21 : if (space) { str++; len--; space = 0; }
1814 : }
1815 13363 : out_term_color(out, c_OUTPUT);
1816 13363 : out_puts(out, str);
1817 13363 : if (!len || str[len-1] != '\n') out_putc(out, '\n');
1818 13363 : if (space) { linelen++; len--; }
1819 13363 : out_term_color(out, c_ERR);
1820 13363 : if (prefix) { out_puts(out, prefix); linelen -= prelen; }
1821 220275 : for (i=0; i<linelen; i++) out_putc(out, ' ');
1822 13363 : out_putc(out, '^');
1823 246862 : for (i=0; i<len; i++) out_putc(out, '-');
1824 : }
1825 18825 : pari_free(word);
1826 18825 : }
1827 :
1828 : #define CONTEXT_LEN 46
1829 : #define MAX_TERM_COLOR 16
1830 : /* Outputs a beautiful error message (not \n terminated)
1831 : * msg is errmessage to print.
1832 : * s points to the offending chars.
1833 : * entry tells how much we can go back from s[0] */
1834 : void
1835 13426 : print_errcontext(PariOUT *out,
1836 : const char *msg, const char *s, const char *entry)
1837 : {
1838 13426 : const long MAX_PAST = 25;
1839 13426 : long past = s - entry, future, lmsg;
1840 : char str[CONTEXT_LEN + 1 + 1], pre[MAX_TERM_COLOR + 8 + 1];
1841 : char *buf, *t;
1842 :
1843 13426 : if (!s || !entry) { print_prefixed_text(out, msg," *** ",NULL); return; }
1844 :
1845 : /* message + context */
1846 13363 : lmsg = strlen(msg);
1847 : /* msg + past + ': ' + '...' + term_get_color + \0 */
1848 13363 : t = buf = (char*)pari_malloc(lmsg + MAX_PAST + 2 + 3 + MAX_TERM_COLOR + 1);
1849 13363 : memcpy(t, msg, lmsg); t += lmsg;
1850 13363 : strcpy(t, ": "); t += 2;
1851 13363 : if (past <= 0) past = 0;
1852 : else
1853 : {
1854 1435 : if (past > MAX_PAST) { past = MAX_PAST; strcpy(t, "..."); t += 3; }
1855 1435 : term_get_color(t, c_OUTPUT);
1856 1435 : t += strlen(t);
1857 1435 : memcpy(t, s - past, past); t[past] = 0;
1858 : }
1859 :
1860 : /* suffix (past arrow) */
1861 13363 : t = str; if (!past) *t++ = ' ';
1862 13363 : future = CONTEXT_LEN - past;
1863 13363 : strncpy(t, s, future); t[future] = 0;
1864 : /* prefix '***' */
1865 13363 : term_get_color(pre, c_ERR);
1866 13363 : strcat(pre, " *** ");
1867 : /* now print */
1868 13363 : print_prefixed_text(out, buf, pre, str);
1869 13363 : pari_free(buf);
1870 : }
1871 :
1872 : /********************************************************************/
1873 : /** **/
1874 : /** GEN <---> CHARACTER STRINGS **/
1875 : /** **/
1876 : /********************************************************************/
1877 : static OUT_FUN
1878 209616 : get_fun(long flag)
1879 : {
1880 209616 : switch(flag) {
1881 148424 : case f_RAW : return bruti;
1882 172 : case f_TEX : return texi;
1883 61020 : default: return matbruti;
1884 : }
1885 : }
1886 :
1887 : /* not stack clean */
1888 : static char *
1889 72313 : stack_GENtostr_fun(GEN x, pariout_t *T, OUT_FUN out)
1890 : {
1891 72313 : pari_str S; str_init(&S, 1);
1892 72313 : out(x, T, &S); *S.cur = 0;
1893 72313 : return S.string;
1894 : }
1895 : /* same but remove quotes "" around t_STR */
1896 : static char *
1897 26332 : stack_GENtostr_fun_unquoted(GEN x, pariout_t *T, OUT_FUN out)
1898 26332 : { return (typ(x)==t_STR)? GSTR(x): stack_GENtostr_fun(x, T, out); }
1899 :
1900 : /* stack-clean: pari-malloc'ed */
1901 : static char *
1902 746 : GENtostr_fun(GEN x, pariout_t *T, OUT_FUN out)
1903 : {
1904 746 : pari_sp av = avma;
1905 746 : pari_str S; str_init(&S, 0);
1906 746 : out(x, T, &S); *S.cur = 0;
1907 746 : set_avma(av); return S.string;
1908 : }
1909 : /* returns a malloc-ed string, which should be freed after usage */
1910 : /* Returns pari_malloc()ed string */
1911 : char *
1912 4 : GENtostr(GEN x)
1913 4 : { return GENtostr_fun(x, GP_DATA->fmt, get_fun(GP_DATA->fmt->prettyp)); }
1914 : char *
1915 0 : GENtoTeXstr(GEN x) { return GENtostr_fun(x, GP_DATA->fmt, &texi); }
1916 : char *
1917 26332 : GENtostr_unquoted(GEN x)
1918 26332 : { return stack_GENtostr_fun_unquoted(x, GP_DATA->fmt, &bruti); }
1919 : /* alloc-ed on PARI stack */
1920 : char *
1921 3717 : GENtostr_raw(GEN x) { return stack_GENtostr_fun(x,GP_DATA->fmt,&bruti); }
1922 :
1923 : GEN
1924 742 : GENtoGENstr(GEN x)
1925 : {
1926 742 : char *s = GENtostr_fun(x, GP_DATA->fmt, &bruti);
1927 742 : GEN z = strtoGENstr(s); pari_free(s); return z;
1928 : }
1929 : GEN
1930 0 : GENtoGENstr_nospace(GEN x)
1931 : {
1932 0 : pariout_t T = *(GP_DATA->fmt);
1933 : char *s;
1934 : GEN z;
1935 0 : T.sp = 0;
1936 0 : s = GENtostr_fun(x, &T, &bruti);
1937 0 : z = strtoGENstr(s); pari_free(s); return z;
1938 : }
1939 :
1940 : /********************************************************************/
1941 : /** **/
1942 : /** WRITE AN INTEGER **/
1943 : /** **/
1944 : /********************************************************************/
1945 : char *
1946 6804 : itostr(GEN x) {
1947 6804 : long sx = signe(x), l;
1948 6804 : return sx? itostr_sign(x, sx, &l): zerotostr();
1949 : }
1950 :
1951 : /* x != 0 t_INT, write abs(x) to S */
1952 : static void
1953 588042 : str_absint(pari_str *S, GEN x)
1954 : {
1955 : pari_sp av;
1956 : long l;
1957 588042 : str_alloc(S, lgefint(x)); /* careful ! */
1958 588042 : av = avma;
1959 588042 : str_puts(S, itostr_sign(x, 1, &l)); set_avma(av);
1960 588042 : }
1961 :
1962 : #define putsigne_nosp(S, x) str_putc(S, (x>0)? '+' : '-')
1963 : #define putsigne(S, x) str_puts(S, (x>0)? " + " : " - ")
1964 : #define sp_sign_sp(T,S, x) ((T)->sp? putsigne(S,x): putsigne_nosp(S,x))
1965 : #define semicolon_sp(T,S) ((T)->sp? str_puts(S, "; "): str_putc(S, ';'))
1966 : #define comma_sp(T,S) ((T)->sp? str_puts(S, ", "): str_putc(S, ','))
1967 :
1968 : /* print e to S (more efficient than sprintf) */
1969 : static void
1970 187633 : str_ulong(pari_str *S, ulong e)
1971 : {
1972 187633 : if (e == 0) str_putc(S, '0');
1973 : else
1974 : {
1975 181032 : char buf[21], *p = buf + numberof(buf);
1976 181032 : *--p = 0;
1977 181032 : if (e > 9) {
1978 : do
1979 38342 : *--p = "0123456789"[e % 10];
1980 38342 : while ((e /= 10) > 9);
1981 : }
1982 181032 : *--p = "0123456789"[e];
1983 181032 : str_puts(S, p);
1984 : }
1985 187633 : }
1986 : static void
1987 187633 : str_long(pari_str *S, long e)
1988 : {
1989 187633 : if (e >= 0) str_ulong(S, (ulong)e);
1990 2401 : else { str_putc(S, '-'); str_ulong(S, -(ulong)e); }
1991 187633 : }
1992 :
1993 : static void
1994 8152 : wr_vecsmall(pariout_t *T, pari_str *S, GEN g)
1995 : {
1996 : long i, l;
1997 8152 : str_puts(S, "Vecsmall(["); l = lg(g);
1998 40609 : for (i=1; i<l; i++)
1999 : {
2000 32457 : str_long(S, g[i]);
2001 32457 : if (i<l-1) comma_sp(T,S);
2002 : }
2003 8152 : str_puts(S, "])");
2004 8152 : }
2005 :
2006 : /********************************************************************/
2007 : /** **/
2008 : /** HEXADECIMAL OUTPUT **/
2009 : /** **/
2010 : /********************************************************************/
2011 : /* English ordinal numbers */
2012 : char *
2013 0 : uordinal(ulong i)
2014 : {
2015 0 : const char *suff[] = {"st","nd","rd","th"};
2016 0 : char *s = stack_malloc(23);
2017 0 : long k = 3;
2018 0 : switch (i%10)
2019 : {
2020 0 : case 1: if (i%100!=11) k = 0;
2021 0 : break;
2022 0 : case 2: if (i%100!=12) k = 1;
2023 0 : break;
2024 0 : case 3: if (i%100!=13) k = 2;
2025 0 : break;
2026 : }
2027 0 : sprintf(s, "%lu%s", i, suff[k]); return s;
2028 : }
2029 :
2030 : static char
2031 0 : vsigne(GEN x)
2032 : {
2033 0 : long s = signe(x);
2034 0 : if (!s) return '0';
2035 0 : return (s > 0) ? '+' : '-';
2036 : }
2037 :
2038 : static void
2039 0 : blancs(long nb) { while (nb-- > 0) pari_putc(' '); }
2040 :
2041 : /* write an "address" */
2042 : static void
2043 0 : str_addr(pari_str *S, ulong x)
2044 0 : { char s[128]; sprintf(s,"%0*lx", BITS_IN_LONG/4, x); str_puts(S, s); }
2045 : static void
2046 0 : dbg_addr(ulong x) { pari_printf("[&=%0*lx] ", BITS_IN_LONG/4, x); }
2047 : /* write a "word" */
2048 : static void
2049 0 : dbg_word(ulong x) { pari_printf("%0*lx ", BITS_IN_LONG/4, x); }
2050 :
2051 : /* bl: indent level */
2052 : static void
2053 0 : dbg(GEN x, long nb, long bl)
2054 : {
2055 : long tx,i,j,e,dx,lx;
2056 :
2057 0 : if (!x) { pari_puts("NULL\n"); return; }
2058 0 : tx = typ(x);
2059 0 : if (tx == t_INT && x == gen_0) { pari_puts("gen_0\n"); return; }
2060 0 : dbg_addr((ulong)x);
2061 :
2062 0 : lx = lg(x);
2063 0 : pari_printf("%s(lg=%ld%s):",type_name(tx)+2,lx,isclone(x)? ",CLONE" : "");
2064 0 : dbg_word(x[0]);
2065 0 : if (! is_recursive_t(tx)) /* t_INT, t_REAL, t_STR, t_VECSMALL */
2066 : {
2067 0 : if (tx == t_STR)
2068 0 : pari_puts("chars:");
2069 0 : else if (tx == t_INT)
2070 : {
2071 0 : lx = lgefint(x);
2072 0 : pari_printf("(%c,lgefint=%ld):", vsigne(x), lx);
2073 : }
2074 0 : else if (tx == t_REAL)
2075 0 : pari_printf("(%c,expo=%ld):", vsigne(x), expo(x));
2076 0 : if (nb < 0) nb = lx;
2077 0 : for (i=1; i < nb; i++) dbg_word(x[i]);
2078 0 : pari_putc('\n'); return;
2079 : }
2080 :
2081 0 : if (tx == t_PADIC)
2082 0 : pari_printf("(precp=%ld,valp=%ld):", precp(x), valp(x));
2083 0 : else if (tx == t_POL)
2084 0 : pari_printf("(%c,varn=%ld):", vsigne(x), varn(x));
2085 0 : else if (tx == t_SER)
2086 0 : pari_printf("(%c,varn=%ld,prec=%ld,valser=%ld):",
2087 0 : vsigne(x), varn(x), lg(x)-2, valser(x));
2088 0 : else if (tx == t_LIST)
2089 : {
2090 0 : pari_printf("(subtyp=%ld,lmax=%ld):", list_typ(x), list_nmax(x));
2091 0 : x = list_data(x); lx = x? lg(x): 1;
2092 0 : tx = t_VEC; /* print list_data as vec */
2093 0 : } else if (tx == t_CLOSURE)
2094 0 : pari_printf("(arity=%ld%s):", closure_arity(x),
2095 0 : closure_is_variadic(x)?"+":"");
2096 0 : for (i=1; i<lx; i++) dbg_word(x[i]);
2097 0 : bl+=2; pari_putc('\n');
2098 0 : switch(tx)
2099 : {
2100 0 : case t_INTMOD: case t_POLMOD:
2101 : {
2102 0 : const char *s = (tx==t_INTMOD)? "int = ": "pol = ";
2103 0 : blancs(bl); pari_puts("mod = "); dbg(gel(x,1),nb,bl);
2104 0 : blancs(bl); pari_puts(s); dbg(gel(x,2),nb,bl);
2105 0 : break;
2106 : }
2107 0 : case t_FRAC: case t_RFRAC:
2108 0 : blancs(bl); pari_puts("num = "); dbg(gel(x,1),nb,bl);
2109 0 : blancs(bl); pari_puts("den = "); dbg(gel(x,2),nb,bl);
2110 0 : break;
2111 :
2112 0 : case t_FFELT:
2113 0 : blancs(bl); pari_puts("pol = "); dbg(gel(x,2),nb,bl);
2114 0 : blancs(bl); pari_puts("mod = "); dbg(gel(x,3),nb,bl);
2115 0 : blancs(bl); pari_puts("p = "); dbg(gel(x,4),nb,bl);
2116 0 : break;
2117 :
2118 0 : case t_COMPLEX:
2119 0 : blancs(bl); pari_puts("real = "); dbg(gel(x,1),nb,bl);
2120 0 : blancs(bl); pari_puts("imag = "); dbg(gel(x,2),nb,bl);
2121 0 : break;
2122 :
2123 0 : case t_PADIC:
2124 0 : blancs(bl); pari_puts(" p : "); dbg(padic_p(x),nb,bl);
2125 0 : blancs(bl); pari_puts("p^l : "); dbg(padic_pd(x),nb,bl);
2126 0 : blancs(bl); pari_puts(" I : "); dbg(padic_u(x),nb,bl);
2127 0 : break;
2128 :
2129 0 : case t_QUAD:
2130 0 : blancs(bl); pari_puts("pol = "); dbg(gel(x,1),nb,bl);
2131 0 : blancs(bl); pari_puts("real = "); dbg(gel(x,2),nb,bl);
2132 0 : blancs(bl); pari_puts("imag = "); dbg(gel(x,3),nb,bl);
2133 0 : break;
2134 :
2135 0 : case t_POL: case t_SER:
2136 0 : e = (tx==t_SER)? valser(x): 0;
2137 0 : for (i=2; i<lx; i++)
2138 : {
2139 0 : blancs(bl); pari_printf("coef of degree %ld = ",e);
2140 0 : e++; dbg(gel(x,i),nb,bl);
2141 : }
2142 0 : break;
2143 :
2144 0 : case t_QFB: case t_VEC: case t_COL:
2145 0 : for (i=1; i<lx; i++)
2146 : {
2147 0 : blancs(bl); pari_printf("%s component = ",uordinal(i));
2148 0 : dbg(gel(x,i),nb,bl);
2149 : }
2150 0 : break;
2151 :
2152 0 : case t_CLOSURE:
2153 0 : blancs(bl); pari_puts("code = "); dbg(closure_get_code(x),nb,bl);
2154 0 : blancs(bl); pari_puts("operand = "); dbg(closure_get_oper(x),nb,bl);
2155 0 : blancs(bl); pari_puts("data = "); dbg(closure_get_data(x),nb,bl);
2156 0 : blancs(bl); pari_puts("dbg/frpc/fram = "); dbg(closure_get_dbg(x),nb,bl);
2157 0 : if (lg(x)>=7)
2158 : {
2159 0 : blancs(bl); pari_puts("text = "); dbg(closure_get_text(x),nb,bl);
2160 0 : if (lg(x)>=8)
2161 : {
2162 0 : blancs(bl); pari_puts("frame = "); dbg(closure_get_frame(x),nb,bl);
2163 : }
2164 : }
2165 0 : break;
2166 :
2167 0 : case t_ERROR:
2168 0 : blancs(bl);
2169 0 : pari_printf("error type = %s\n", numerr_name(err_get_num(x)));
2170 0 : for (i=2; i<lx; i++)
2171 : {
2172 0 : blancs(bl); pari_printf("%s component = ",uordinal(i-1));
2173 0 : dbg(gel(x,i),nb,bl);
2174 : }
2175 0 : break;
2176 :
2177 0 : case t_INFINITY:
2178 0 : blancs(bl); pari_printf("1st component = ");
2179 0 : dbg(gel(x,1),nb,bl);
2180 0 : break;
2181 :
2182 0 : case t_MAT:
2183 : {
2184 0 : GEN c = gel(x,1);
2185 0 : if (lx == 1) return;
2186 0 : if (typ(c) == t_VECSMALL)
2187 : {
2188 0 : for (i = 1; i < lx; i++)
2189 : {
2190 0 : blancs(bl); pari_printf("%s column = ",uordinal(i));
2191 0 : dbg(gel(x,i),nb,bl);
2192 : }
2193 : }
2194 : else
2195 : {
2196 0 : dx = lg(c);
2197 0 : for (i=1; i<dx; i++)
2198 0 : for (j=1; j<lx; j++)
2199 : {
2200 0 : blancs(bl); pari_printf("mat(%ld,%ld) = ",i,j);
2201 0 : dbg(gcoeff(x,i,j),nb,bl);
2202 : }
2203 : }
2204 : }
2205 : }
2206 : }
2207 :
2208 : void
2209 0 : dbgGEN(GEN x, long nb) { dbg(x,nb,0); }
2210 :
2211 : static void
2212 0 : print_entree(entree *ep)
2213 : {
2214 0 : pari_printf(" %s ",ep->name); dbg_addr((ulong)ep);
2215 0 : pari_printf(": hash = %ld [%ld]\n", ep->hash % functions_tblsz, ep->hash);
2216 0 : pari_printf(" menu = %2ld, code = %-10s",
2217 0 : ep->menu, ep->code? ep->code: "NULL");
2218 0 : if (ep->next)
2219 : {
2220 0 : pari_printf("next = %s ",(ep->next)->name);
2221 0 : dbg_addr((ulong)ep->next);
2222 : }
2223 0 : pari_puts("\n");
2224 0 : }
2225 :
2226 : /* s = digit n : list of entrees in functions_hash[n] (s = $: last entry)
2227 : * = range m-n: functions_hash[m..n]
2228 : * = identifier: entree for that identifier */
2229 : void
2230 0 : print_functions_hash(const char *s)
2231 : {
2232 : long m, n, Max, Total;
2233 : entree *ep;
2234 :
2235 0 : if (isdigit((unsigned char)*s) || *s == '$')
2236 : {
2237 0 : m = functions_tblsz-1; n = atol(s);
2238 0 : if (*s=='$') n = m;
2239 0 : if (m<n) pari_err(e_MISC,"invalid range in print_functions_hash");
2240 0 : while (isdigit((unsigned char)*s)) s++;
2241 :
2242 0 : if (*s++ != '-') m = n;
2243 : else
2244 : {
2245 0 : if (*s !='$') m = minss(atol(s),m);
2246 0 : if (m<n) pari_err(e_MISC,"invalid range in print_functions_hash");
2247 : }
2248 :
2249 0 : for(; n<=m; n++)
2250 : {
2251 0 : pari_printf("*** hashcode = %lu\n",n);
2252 0 : for (ep=functions_hash[n]; ep; ep=ep->next) print_entree(ep);
2253 : }
2254 0 : return;
2255 : }
2256 0 : if (is_keyword_char(*s))
2257 : {
2258 0 : ep = is_entry(s);
2259 0 : if (!ep) pari_err(e_MISC,"no such function");
2260 0 : print_entree(ep); return;
2261 : }
2262 0 : if (*s=='-')
2263 : {
2264 0 : for (n=0; n<functions_tblsz; n++)
2265 : {
2266 0 : m=0;
2267 0 : for (ep=functions_hash[n]; ep; ep=ep->next) m++;
2268 0 : pari_printf("%3ld:%3ld ",n,m);
2269 0 : if (n%9 == 8) pari_putc('\n');
2270 : }
2271 0 : pari_putc('\n'); return;
2272 : }
2273 0 : Max = Total = 0;
2274 0 : for (n=0; n<functions_tblsz; n++)
2275 : {
2276 0 : long cnt = 0;
2277 0 : for (ep=functions_hash[n]; ep; ep=ep->next) { print_entree(ep); cnt++; }
2278 0 : Total += cnt;
2279 0 : if (cnt > Max) Max = cnt;
2280 : }
2281 0 : pari_printf("Total: %ld, Max: %ld\n", Total, Max);
2282 : }
2283 :
2284 : /********************************************************************/
2285 : /** **/
2286 : /** FORMATTED OUTPUT **/
2287 : /** **/
2288 : /********************************************************************/
2289 : static const char *
2290 101646 : get_var(long v, char *buf)
2291 : {
2292 101646 : entree *ep = varentries[v];
2293 101646 : if (ep) return (char*)ep->name;
2294 0 : sprintf(buf,"t%d",(int)v); return buf;
2295 : }
2296 :
2297 : static void
2298 0 : do_append(char **sp, char c, char *last, int count)
2299 : {
2300 0 : if (*sp + count > last)
2301 0 : pari_err(e_MISC, "TeX variable name too long");
2302 0 : while (count--)
2303 0 : *(*sp)++ = c;
2304 0 : }
2305 :
2306 : static char *
2307 105 : get_texvar(long v, char *buf, unsigned int len)
2308 : {
2309 105 : entree *ep = varentries[v];
2310 105 : char *t = buf, *e = buf + len - 1;
2311 : const char *s;
2312 :
2313 105 : if (!ep) pari_err(e_MISC, "this object uses debugging variables");
2314 105 : s = ep->name;
2315 105 : if (strlen(s) >= len) pari_err(e_MISC, "TeX variable name too long");
2316 210 : while (isalpha((unsigned char)*s)) *t++ = *s++;
2317 105 : *t = 0;
2318 105 : if (isdigit((unsigned char)*s) || *s == '_') {
2319 0 : int seen1 = 0, seen = 0;
2320 :
2321 : /* Skip until the first non-underscore */
2322 0 : while (*s == '_') s++, seen++;
2323 :
2324 : /* Special-case integers and empty subscript */
2325 0 : if (*s == 0 || isdigit((unsigned char)*s))
2326 0 : seen++;
2327 :
2328 0 : do_append(&t, '_', e, 1);
2329 0 : do_append(&t, '{', e, 1);
2330 0 : do_append(&t, '[', e, seen - 1);
2331 : while (1) {
2332 0 : if (*s == '_')
2333 0 : seen1++, s++;
2334 : else {
2335 0 : if (seen1) {
2336 0 : do_append(&t, ']', e, (seen >= seen1 ? seen1 : seen) - 1);
2337 0 : do_append(&t, ',', e, 1);
2338 0 : do_append(&t, '[', e, seen1 - 1);
2339 0 : if (seen1 > seen)
2340 0 : seen = seen1;
2341 0 : seen1 = 0;
2342 : }
2343 0 : if (*s == 0)
2344 0 : break;
2345 0 : do_append(&t, *s++, e, 1);
2346 : }
2347 : }
2348 0 : do_append(&t, ']', e, seen - 1);
2349 0 : do_append(&t, '}', e, 1);
2350 0 : *t = 0;
2351 : }
2352 105 : return buf;
2353 : }
2354 :
2355 : void
2356 0 : dbg_pari_heap(void)
2357 : {
2358 : long nu, l, u, s;
2359 0 : pari_sp av = avma;
2360 0 : GEN adr = getheap();
2361 0 : pari_sp top = pari_mainstack->top, bot = pari_mainstack->bot;
2362 :
2363 0 : nu = (top-avma)/sizeof(long);
2364 0 : l = pari_mainstack->size/sizeof(long);
2365 0 : pari_printf("\n Top : %lx Bottom : %lx Current stack : %lx\n",
2366 : top, bot, avma);
2367 0 : pari_printf(" Used : %ld long words (%ld K)\n",
2368 0 : nu, nu/1024*sizeof(long));
2369 0 : pari_printf(" Available : %ld long words (%ld K)\n",
2370 0 : (l-nu), (l-nu)/1024*sizeof(long));
2371 0 : pari_printf(" Occupation of the PARI stack : %6.2f percent\n", 100.0*nu/l);
2372 0 : pari_printf(" %ld objects on heap occupy %ld long words\n\n",
2373 0 : itos(gel(adr,1)), itos(gel(adr,2)));
2374 0 : u = pari_var_next();
2375 0 : s = MAXVARN - pari_var_next_temp();
2376 0 : pari_printf(" %ld variable names used (%ld user + %ld private) out of %d\n\n",
2377 : u+s, u, s, MAXVARN);
2378 0 : set_avma(av);
2379 0 : }
2380 :
2381 : /* is to be printed as '0' */
2382 : static long
2383 3727189 : isnull(GEN g)
2384 : {
2385 : long i;
2386 3727189 : switch (typ(g))
2387 : {
2388 3093547 : case t_INT:
2389 3093547 : return !signe(g);
2390 12670 : case t_COMPLEX:
2391 12670 : return isnull(gel(g,1)) && isnull(gel(g,2));
2392 15939 : case t_FFELT:
2393 15939 : return FF_equal0(g);
2394 2093 : case t_QUAD:
2395 2093 : return isnull(gel(g,2)) && isnull(gel(g,3));
2396 102141 : case t_FRAC: case t_RFRAC:
2397 102141 : return isnull(gel(g,1));
2398 175405 : case t_POL:
2399 175503 : for (i=lg(g)-1; i>1; i--)
2400 162126 : if (!isnull(gel(g,i))) return 0;
2401 13377 : return 1;
2402 : }
2403 325394 : return 0;
2404 : }
2405 : /* 0 coeff to be omitted in t_POL ? */
2406 : static int
2407 1761542 : isnull_for_pol(GEN g)
2408 : {
2409 1761542 : switch(typ(g))
2410 : {
2411 8253 : case t_INTMOD: return !signe(gel(g,2));
2412 5418 : case t_POLMOD: return isnull_for_pol(gel(g,2));
2413 1747871 : default: return isnull(g);
2414 : }
2415 : }
2416 :
2417 : /* return 1 or -1 if g is 1 or -1, 0 otherwise*/
2418 : static long
2419 1658375 : isone(GEN g)
2420 : {
2421 : long i;
2422 1658375 : switch (typ(g))
2423 : {
2424 1135557 : case t_INT:
2425 1135557 : return (signe(g) && is_pm1(g))? signe(g): 0;
2426 9093 : case t_FFELT:
2427 9093 : return FF_equal1(g);
2428 12257 : case t_COMPLEX:
2429 12257 : return isnull(gel(g,2))? isone(gel(g,1)): 0;
2430 1519 : case t_QUAD:
2431 1519 : return isnull(gel(g,3))? isone(gel(g,2)): 0;
2432 79783 : case t_FRAC: case t_RFRAC:
2433 79783 : return isone(gel(g,1)) * isone(gel(g,2));
2434 124501 : case t_POL:
2435 124501 : if (!signe(g)) return 0;
2436 123661 : for (i=lg(g)-1; i>2; i--)
2437 115205 : if (!isnull(gel(g,i))) return 0;
2438 8456 : return isone(gel(g,2));
2439 : }
2440 295665 : return 0;
2441 : }
2442 :
2443 : /* if g is a "monomial", return its sign, 0 otherwise */
2444 : static long
2445 298162 : isfactor(GEN g)
2446 : {
2447 : long i,deja,sig;
2448 298162 : switch(typ(g))
2449 : {
2450 223318 : case t_INT: case t_REAL:
2451 223318 : return (signe(g)<0)? -1: 1;
2452 28063 : case t_FRAC: case t_RFRAC:
2453 28063 : return isfactor(gel(g,1));
2454 2366 : case t_FFELT:
2455 2366 : return isfactor(FF_to_FpXQ_i(g));
2456 2114 : case t_COMPLEX:
2457 2114 : if (isnull(gel(g,1))) return isfactor(gel(g,2));
2458 1442 : if (isnull(gel(g,2))) return isfactor(gel(g,1));
2459 1442 : return 0;
2460 1960 : case t_PADIC:
2461 1960 : return !signe(padic_u(g));
2462 532 : case t_QUAD:
2463 532 : if (isnull(gel(g,2))) return isfactor(gel(g,3));
2464 385 : if (isnull(gel(g,3))) return isfactor(gel(g,2));
2465 385 : return 0;
2466 27587 : case t_POL: deja = 0; sig = 1;
2467 77861 : for (i=lg(g)-1; i>1; i--)
2468 65100 : if (!isnull_for_pol(gel(g,i)))
2469 : {
2470 42161 : if (deja) return 0;
2471 27335 : sig=isfactor(gel(g,i)); deja=1;
2472 : }
2473 12761 : return sig? sig: 1;
2474 105 : case t_SER:
2475 490 : for (i=lg(g)-1; i>1; i--)
2476 469 : if (!isnull(gel(g,i))) return 0;
2477 21 : return 1;
2478 0 : case t_CLOSURE:
2479 0 : return 0;
2480 : }
2481 12117 : return 1;
2482 : }
2483 :
2484 : /* return 1 if g is a "truc" (see anal.c) */
2485 : static long
2486 53256 : isdenom(GEN g)
2487 : {
2488 : long i,deja;
2489 53256 : switch(typ(g))
2490 : {
2491 0 : case t_FRAC: case t_RFRAC:
2492 0 : return 0;
2493 0 : case t_COMPLEX: return isnull(gel(g,2));
2494 0 : case t_PADIC: return !signe(padic_u(g));
2495 0 : case t_QUAD: return isnull(gel(g,3));
2496 :
2497 1785 : case t_POL: deja = 0;
2498 18368 : for (i=lg(g)-1; i>1; i--)
2499 17843 : if (!isnull(gel(g,i)))
2500 : {
2501 2415 : if (deja) return 0;
2502 1785 : if (i==2) return isdenom(gel(g,2));
2503 1785 : if (!isone(gel(g,i))) return 0;
2504 1155 : deja=1;
2505 : }
2506 525 : return 1;
2507 0 : case t_SER:
2508 0 : for (i=lg(g)-1; i>1; i--)
2509 0 : if (!isnull(gel(g,i))) return 0;
2510 : }
2511 51471 : return 1;
2512 : }
2513 :
2514 : /********************************************************************/
2515 : /** **/
2516 : /** RAW OUTPUT **/
2517 : /** **/
2518 : /********************************************************************/
2519 : /* ^e */
2520 : static void
2521 210 : texexpo(pari_str *S, long e)
2522 : {
2523 210 : if (e != 1) {
2524 105 : str_putc(S, '^');
2525 105 : if (e >= 0 && e < 10)
2526 105 : { str_putc(S, '0' + e); }
2527 : else
2528 : {
2529 0 : str_putc(S, '{'); str_long(S, e); str_putc(S, '}');
2530 : }
2531 : }
2532 210 : }
2533 : static void
2534 240169 : wrexpo(pari_str *S, long e)
2535 240169 : { if (e != 1) { str_putc(S, '^'); str_long(S, e); } }
2536 :
2537 : /* v^e */
2538 : static void
2539 240169 : VpowE(pari_str *S, const char *v, long e) { str_puts(S, v); wrexpo(S,e); }
2540 : static void
2541 210 : texVpowE(pari_str *S, const char *v, long e) { str_puts(S, v); texexpo(S,e); }
2542 : static void
2543 225266 : monome(pari_str *S, const char *v, long e)
2544 225266 : { if (e) VpowE(S, v, e); else str_putc(S, '1'); }
2545 : static void
2546 203 : texnome(pari_str *S, const char *v, long e)
2547 203 : { if (e) texVpowE(S, v, e); else str_putc(S, '1'); }
2548 :
2549 : /* ( a ) */
2550 : static void
2551 15995 : paren(pariout_t *T, pari_str *S, GEN a)
2552 15995 : { str_putc(S, '('); bruti(a,T,S); str_putc(S, ')'); }
2553 : static void
2554 0 : texparen(pariout_t *T, pari_str *S, GEN a)
2555 : {
2556 0 : if (T->TeXstyle & TEXSTYLE_PAREN)
2557 0 : str_puts(S, " (");
2558 : else
2559 0 : str_puts(S, " \\left(");
2560 0 : texi(a,T,S);
2561 0 : if (T->TeXstyle & TEXSTYLE_PAREN)
2562 0 : str_puts(S, ") ");
2563 : else
2564 0 : str_puts(S, "\\right) ");
2565 0 : }
2566 :
2567 : /* * v^d */
2568 : static void
2569 140 : times_texnome(pari_str *S, const char *v, long d)
2570 140 : { if (d) { str_puts(S, "\\*"); texnome(S,v,d); } }
2571 : static void
2572 186172 : times_monome(pari_str *S, const char *v, long d)
2573 186172 : { if (d) { str_putc(S, '*'); monome(S,v,d); } }
2574 :
2575 : /* write a * v^d */
2576 : static void
2577 180516 : wr_monome(pariout_t *T, pari_str *S, GEN a, const char *v, long d)
2578 : {
2579 180516 : long sig = isone(a);
2580 :
2581 180516 : if (sig) {
2582 31913 : sp_sign_sp(T,S,sig); monome(S,v,d);
2583 : } else {
2584 148603 : sig = isfactor(a);
2585 148603 : if (sig) { sp_sign_sp(T,S,sig); bruti_sign(a,T,S,0); }
2586 13055 : else { sp_sign_sp(T,S,1); paren(T,S, a); }
2587 148603 : times_monome(S, v, d);
2588 : }
2589 180516 : }
2590 : static void
2591 105 : wr_texnome(pariout_t *T, pari_str *S, GEN a, const char *v, long d)
2592 : {
2593 105 : long sig = isone(a);
2594 :
2595 105 : str_putc(S, '\n'); /* Avoid TeX buffer overflow */
2596 105 : if (T->TeXstyle & TEXSTYLE_BREAK) str_puts(S, "\\PARIbreak ");
2597 :
2598 105 : if (sig) {
2599 14 : putsigne(S,sig); texnome(S,v,d);
2600 : } else {
2601 91 : sig = isfactor(a);
2602 91 : if (sig) { putsigne(S,sig); texi_sign(a,T,S,0); }
2603 0 : else { str_puts(S, " +"); texparen(T,S, a); }
2604 91 : times_texnome(S, v, d);
2605 : }
2606 105 : }
2607 :
2608 : static void
2609 102493 : wr_lead_monome(pariout_t *T, pari_str *S, GEN a,const char *v, long d, int addsign)
2610 : {
2611 102493 : long sig = isone(a);
2612 102493 : if (sig) {
2613 64924 : if (addsign && sig<0) str_putc(S, '-');
2614 64924 : monome(S,v,d);
2615 : } else {
2616 37569 : if (isfactor(a)) bruti_sign(a,T,S,addsign);
2617 2940 : else paren(T,S, a);
2618 37569 : times_monome(S, v, d);
2619 : }
2620 102493 : }
2621 : static void
2622 119 : wr_lead_texnome(pariout_t *T, pari_str *S, GEN a,const char *v, long d, int addsign)
2623 : {
2624 119 : long sig = isone(a);
2625 119 : if (sig) {
2626 70 : if (addsign && sig<0) str_putc(S, '-');
2627 70 : texnome(S,v,d);
2628 : } else {
2629 49 : if (isfactor(a)) texi_sign(a,T,S,addsign);
2630 0 : else texparen(T,S, a);
2631 49 : times_texnome(S, v, d);
2632 : }
2633 119 : }
2634 :
2635 : static void
2636 0 : prints(GEN g, pariout_t *T, pari_str *S)
2637 0 : { (void)T; str_long(S, (long)g); }
2638 :
2639 : static void
2640 14942 : quote_string(pari_str *S, char *s)
2641 : {
2642 14942 : str_putc(S, '"');
2643 513244 : while (*s)
2644 : {
2645 498302 : char c=*s++;
2646 498302 : if (c=='\\' || c=='"' || c=='\033' || c=='\n' || c=='\t')
2647 : {
2648 2454 : str_putc(S, '\\');
2649 2454 : switch(c)
2650 : {
2651 2146 : case '\\': case '"': break;
2652 308 : case '\n': c='n'; break;
2653 0 : case '\033': c='e'; break;
2654 0 : case '\t': c='t'; break;
2655 : }
2656 : }
2657 498302 : str_putc(S, c);
2658 : }
2659 14942 : str_putc(S, '"');
2660 14942 : }
2661 :
2662 : static int
2663 1428529 : print_0_or_pm1(GEN g, pari_str *S, int addsign)
2664 : {
2665 : long r;
2666 1428529 : if (!g) { str_puts(S, "NULL"); return 1; }
2667 1428529 : if (isnull(g)) { str_putc(S, '0'); return 1; }
2668 1205258 : r = isone(g);
2669 1205258 : if (r)
2670 : {
2671 196010 : if (addsign && r<0) str_putc(S, '-');
2672 196010 : str_putc(S, '1'); return 1;
2673 : }
2674 1009248 : return 0;
2675 : }
2676 :
2677 : static void
2678 4613 : print_precontext(GEN g, pari_str *S, long tex)
2679 : {
2680 4613 : if (lg(g)<8 || lg(gel(g,7))==1) return;
2681 : else
2682 : {
2683 0 : long i, n = closure_arity(g);
2684 0 : str_puts(S,"(");
2685 0 : for(i=1; i<=n; i++)
2686 : {
2687 0 : str_puts(S,"v");
2688 0 : if (tex) str_puts(S,"_{");
2689 0 : str_ulong(S,i);
2690 0 : if (tex) str_puts(S,"}");
2691 0 : if (i < n) str_puts(S,",");
2692 : }
2693 0 : str_puts(S,")->");
2694 : }
2695 : }
2696 :
2697 : static void
2698 5410 : print_context(GEN g, pariout_t *T, pari_str *S, long tex)
2699 : {
2700 5410 : GEN str = closure_get_text(g);
2701 5410 : if (lg(g)<8 || lg(gel(g,7))==1) return;
2702 83 : if (typ(str)==t_VEC && lg(gel(closure_get_dbg(g),3)) >= 2)
2703 83 : {
2704 83 : GEN v = closure_get_frame(g), d = gmael(closure_get_dbg(g),3,1);
2705 83 : long i, l = lg(v), n=0;
2706 186 : for(i=1; i<l; i++)
2707 103 : if (gel(d,i))
2708 103 : n++;
2709 83 : if (n==0) return;
2710 83 : str_puts(S,"my(");
2711 186 : for(i=1; i<l; i++)
2712 103 : if (gel(d,i))
2713 : {
2714 103 : entree *ep = (entree*) gel(d,i);
2715 103 : GEN vi = gel(v,l-i);
2716 103 : str_puts(S,ep->name);
2717 103 : if (!isintzero(vi))
2718 : {
2719 103 : str_putc(S,'=');
2720 103 : if (tex) texi(gel(v,l-i),T,S); else bruti(gel(v,l-i),T,S);
2721 : }
2722 103 : if (--n)
2723 20 : str_putc(S,',');
2724 : }
2725 83 : str_puts(S,");");
2726 : }
2727 : else
2728 : {
2729 0 : GEN v = closure_get_frame(g);
2730 0 : long i, l = lg(v), n = closure_arity(g);
2731 0 : str_puts(S,"(");
2732 0 : for(i=1; i<=n; i++)
2733 : {
2734 0 : str_puts(S,"v");
2735 0 : if (tex) str_puts(S,"_{");
2736 0 : str_ulong(S,i);
2737 0 : if (tex) str_puts(S,"}");
2738 0 : str_puts(S,",");
2739 : }
2740 0 : for(i=1; i<l; i++)
2741 : {
2742 0 : if (tex) texi(gel(v,i),T,S); else bruti(gel(v,i),T,S);
2743 0 : if (i<l-1)
2744 0 : str_putc(S,',');
2745 : }
2746 0 : str_puts(S,")");
2747 : }
2748 : }
2749 : static void
2750 392 : mat0n(pari_str *S, long n)
2751 392 : { str_puts(S, "matrix(0,"); str_long(S, n); str_putc(S, ')'); }
2752 :
2753 : static const char *
2754 11088 : cxq_init(GEN g, long tg, GEN *a, GEN *b, char *buf)
2755 : {
2756 11088 : int r = (tg==t_QUAD);
2757 11088 : *a = gel(g,r+1);
2758 11088 : *b = gel(g,r+2); return r? get_var(varn(gel(g,1)), buf): "I";
2759 : }
2760 :
2761 : static void
2762 0 : print_coef(GEN g, long i, long j, pariout_t *T, pari_str *S)
2763 0 : { (void)T; str_long(S, coeff(g,i,j)); }
2764 : static void
2765 239788 : print_gcoef(GEN g, long i, long j, pariout_t *T, pari_str *S)
2766 : {
2767 239788 : GEN gij = gcoeff(g, i, j);
2768 239788 : if (typ(gij)==t_CLOSURE)
2769 28 : { str_putc(S, '('); bruti(gij, T, S); str_putc(S, ')'); }
2770 : else
2771 239760 : bruti(gij, T, S);
2772 239788 : }
2773 :
2774 : static void
2775 1008971 : bruti_intern(GEN g, pariout_t *T, pari_str *S, int addsign)
2776 : {
2777 1008971 : long l,i,j,r, tg = typ(g);
2778 : GEN a,b;
2779 : const char *v;
2780 : char buf[32];
2781 :
2782 1008971 : switch(tg)
2783 : {
2784 573986 : case t_INT:
2785 573986 : if (addsign && signe(g) < 0) str_putc(S, '-');
2786 573986 : str_absint(S, g); break;
2787 34720 : case t_REAL:
2788 : {
2789 : pari_sp av;
2790 34720 : str_alloc(S, lg(g)); /* careful! */
2791 34720 : av = avma;
2792 34720 : if (addsign && signe(g) < 0) str_putc(S, '-');
2793 34720 : str_puts(S, absrtostr(g, T->sp, (char)toupper((unsigned char)T->format), T->sigd) );
2794 34720 : set_avma(av); break;
2795 : }
2796 :
2797 28819 : case t_INTMOD: case t_POLMOD:
2798 28819 : str_puts(S, "Mod(");
2799 28819 : bruti(gel(g,2),T,S); comma_sp(T,S);
2800 28819 : bruti(gel(g,1),T,S); str_putc(S, ')'); break;
2801 :
2802 4431 : case t_FFELT:
2803 4431 : bruti_sign(FF_to_FpXQ_i(g),T,S,addsign);
2804 4431 : break;
2805 :
2806 53256 : case t_FRAC: case t_RFRAC:
2807 53256 : r = isfactor(gel(g,1)); if (!r) str_putc(S, '(');
2808 53256 : bruti_sign(gel(g,1),T,S,addsign);
2809 53256 : if (!r) str_putc(S, ')');
2810 53256 : str_putc(S, '/');
2811 53256 : r = isdenom(gel(g,2)); if (!r) str_putc(S, '(');
2812 53256 : bruti(gel(g,2),T,S);
2813 53256 : if (!r) str_putc(S, ')');
2814 53256 : break;
2815 :
2816 11039 : case t_COMPLEX: case t_QUAD: r = (tg==t_QUAD);
2817 11039 : v = cxq_init(g, tg, &a, &b, buf);
2818 11039 : if (isnull(a))
2819 : {
2820 2254 : wr_lead_monome(T,S,b,v,1,addsign);
2821 5418 : return;
2822 : }
2823 8785 : bruti_sign(a,T,S,addsign);
2824 8785 : if (!isnull(b)) wr_monome(T,S,b,v,1);
2825 8785 : break;
2826 :
2827 96487 : case t_POL: v = get_var(varn(g), buf);
2828 : /* hack: we want g[i] = coeff of degree i. */
2829 96508 : i = degpol(g); g += 2; while (isnull(gel(g,i))) i--;
2830 96487 : wr_lead_monome(T,S,gel(g,i),v,i,addsign);
2831 1762682 : while (i--)
2832 : {
2833 1666195 : a = gel(g,i);
2834 1666195 : if (!isnull_for_pol(a)) wr_monome(T,S,a,v,i);
2835 : }
2836 96487 : break;
2837 :
2838 4151 : case t_SER: v = get_var(varn(g), buf);
2839 4151 : i = valser(g);
2840 4151 : l = lg(g)-2;
2841 4151 : if (l)
2842 : {
2843 : /* See normalizeser(): Mod(0,2)*x^i*(1+O(x)), has valser = i+1 */
2844 3752 : if (l == 1 && !signe(g) && isexactzero(gel(g,2))) i--;
2845 : /* hack: we want g[i] = coeff of degree i */
2846 3752 : l += i; g -= i-2;
2847 3752 : wr_lead_monome(T,S,gel(g,i),v,i,addsign);
2848 28378 : while (++i < l)
2849 : {
2850 24626 : a = gel(g,i);
2851 24626 : if (!isnull_for_pol(a)) wr_monome(T,S,a,v,i);
2852 : }
2853 3752 : sp_sign_sp(T,S,1);
2854 : }
2855 4151 : str_puts(S, "O("); VpowE(S, v, i); str_putc(S, ')'); break;
2856 :
2857 6783 : case t_PADIC:
2858 : {
2859 6783 : GEN p = padic_p(g);
2860 : pari_sp av, av0;
2861 : char *ev;
2862 6783 : str_alloc(S, (precp(g)+1) * lgefint(p)); /* careful! */
2863 6783 : av0 = avma;
2864 6783 : ev = itostr(p);
2865 6783 : av = avma;
2866 6783 : i = valp(g); l = precp(g)+i;
2867 6783 : g = padic_u(g);
2868 40075 : for (; i<l; i++)
2869 : {
2870 33292 : g = dvmdii(g,p,&a);
2871 33292 : if (signe(a))
2872 : {
2873 23100 : if (!i || !is_pm1(a))
2874 : {
2875 14049 : str_absint(S, a); if (i) str_putc(S, '*');
2876 : }
2877 23100 : if (i) VpowE(S, ev,i);
2878 23100 : sp_sign_sp(T,S,1);
2879 : }
2880 33292 : if ((i & 0xff) == 0) g = gc_INT(av,g);
2881 : }
2882 6783 : str_puts(S, "O("); VpowE(S, ev,i); str_putc(S, ')');
2883 6783 : set_avma(av0); break;
2884 : }
2885 :
2886 651 : case t_QFB:
2887 651 : str_puts(S, "Qfb(");
2888 651 : bruti(gel(g,1),T,S); comma_sp(T,S);
2889 651 : bruti(gel(g,2),T,S); comma_sp(T,S);
2890 651 : bruti(gel(g,3),T,S);
2891 651 : str_putc(S, ')'); break;
2892 :
2893 145341 : case t_VEC: case t_COL:
2894 145341 : str_putc(S, '['); l = lg(g);
2895 618414 : for (i=1; i<l; i++)
2896 : {
2897 473073 : bruti(gel(g,i),T,S);
2898 473073 : if (i<l-1) comma_sp(T,S);
2899 : }
2900 145341 : str_putc(S, ']'); if (tg==t_COL) str_putc(S, '~');
2901 145341 : break;
2902 8152 : case t_VECSMALL: wr_vecsmall(T,S,g); break;
2903 :
2904 711 : case t_LIST:
2905 711 : switch (list_typ(g))
2906 : {
2907 599 : case t_LIST_RAW:
2908 599 : str_puts(S, "List([");
2909 599 : g = list_data(g);
2910 599 : l = g? lg(g): 1;
2911 1929 : for (i=1; i<l; i++)
2912 : {
2913 1330 : bruti(gel(g,i),T,S);
2914 1330 : if (i<l-1) comma_sp(T,S);
2915 : }
2916 599 : str_puts(S, "])"); break;
2917 112 : case t_LIST_MAP:
2918 112 : str_puts(S, "Map(");
2919 112 : bruti(maptomat_shallow(g),T,S);
2920 112 : str_puts(S, ")"); break;
2921 : }
2922 711 : break;
2923 6024 : case t_STR:
2924 6024 : quote_string(S, GSTR(g)); break;
2925 8918 : case t_ERROR:
2926 : {
2927 8918 : char *s = pari_err2str(g);
2928 8918 : str_puts(S, "error(");
2929 8918 : quote_string(S, s); pari_free(s);
2930 8918 : str_puts(S, ")"); break;
2931 : }
2932 5403 : case t_CLOSURE:
2933 5403 : if (lg(g)>=7)
2934 : {
2935 5403 : GEN str = closure_get_text(g);
2936 5403 : if (typ(str)==t_STR)
2937 : {
2938 4613 : print_precontext(g, S, 0);
2939 4613 : str_puts(S, GSTR(str));
2940 4613 : print_context(g, T, S, 0);
2941 : }
2942 : else
2943 : {
2944 790 : str_putc(S,'('); str_puts(S,GSTR(gel(str,1)));
2945 790 : str_puts(S,")->");
2946 790 : print_context(g, T, S, 0);
2947 790 : str_puts(S,GSTR(gel(str,2)));
2948 : }
2949 : }
2950 : else
2951 : {
2952 0 : str_puts(S,"{\""); str_puts(S,GSTR(closure_get_code(g)));
2953 0 : str_puts(S,"\","); wr_vecsmall(T,S,closure_get_oper(g));
2954 0 : str_putc(S,','); bruti(gel(g,4),T,S);
2955 0 : str_putc(S,','); bruti(gel(g,5),T,S);
2956 0 : str_putc(S,'}');
2957 : }
2958 5403 : break;
2959 763 : case t_INFINITY: str_puts(S, inf_get_sign(g) == 1? "+oo": "-oo");
2960 763 : break;
2961 :
2962 19336 : case t_MAT:
2963 : {
2964 : void (*print)(GEN,long,long,pariout_t *,pari_str *);
2965 :
2966 19336 : r = lg(g); if (r==1) { str_puts(S, "[;]"); return; }
2967 18391 : l = lgcols(g); if (l==1) { mat0n(S, r-1); return; }
2968 18118 : print = (typ(gel(g,1)) == t_VECSMALL)? print_coef: print_gcoef;
2969 18118 : if (l==2)
2970 : {
2971 5369 : str_puts(S, "Mat(");
2972 5369 : if (r == 2 && (print != print_gcoef || typ(gcoeff(g,1,1)) != t_MAT))
2973 1946 : { print(g, 1, 1,T, S); str_putc(S, ')'); return; }
2974 : }
2975 16172 : str_putc(S, '[');
2976 70763 : for (i=1; i<l; i++)
2977 : {
2978 292433 : for (j=1; j<r; j++)
2979 : {
2980 237842 : print(g, i, j, T, S);
2981 237842 : if (j<r-1) comma_sp(T,S);
2982 : }
2983 54591 : if (i<l-1) semicolon_sp(T,S);
2984 : }
2985 16172 : str_putc(S, ']'); if (l==2) str_putc(S, ')');
2986 16172 : break;
2987 : }
2988 :
2989 0 : default: str_addr(S, *g);
2990 : }
2991 : }
2992 :
2993 : static void
2994 1428013 : bruti_sign(GEN g, pariout_t *T, pari_str *S, int addsign)
2995 : {
2996 1428013 : if (!print_0_or_pm1(g, S, addsign))
2997 1008834 : bruti_intern(g, T, S, addsign);
2998 1428013 : }
2999 :
3000 : static void
3001 61020 : matbruti(GEN g, pariout_t *T, pari_str *S)
3002 : {
3003 61020 : long i, j, r, w, l, *pad = NULL;
3004 : pari_sp av;
3005 : OUT_FUN print;
3006 :
3007 61020 : if (typ(g) != t_MAT) { bruti(g,T,S); return; }
3008 :
3009 4451 : r=lg(g); if (r==1) { str_puts(S, "[;]"); return; }
3010 4220 : l = lgcols(g); if (l==1) { mat0n(S, r-1); return; }
3011 4101 : str_putc(S, '\n');
3012 4101 : print = (typ(gel(g,1)) == t_VECSMALL)? prints: bruti;
3013 4101 : av = avma;
3014 4101 : w = term_width();
3015 4101 : if (2*r < w)
3016 : {
3017 4094 : long lgall = 2; /* opening [ and closing ] */
3018 : pari_sp av2;
3019 : pari_str str;
3020 4094 : pad = cgetg(l*r+1, t_VECSMALL); /* left on stack if (S->use_stack)*/
3021 4094 : av2 = avma;
3022 4094 : str_init(&str, 1);
3023 14832 : for (j=1; j<r; j++)
3024 : {
3025 11052 : GEN col = gel(g,j);
3026 11052 : long maxc = 0;
3027 56529 : for (i=1; i<l; i++)
3028 : {
3029 : long lgs;
3030 45477 : str.cur = str.string;
3031 45477 : print(gel(col,i),T,&str);
3032 45477 : lgs = str.cur - str.string;
3033 45477 : pad[j*l+i] = -lgs;
3034 45477 : if (maxc < lgs) maxc = lgs;
3035 : }
3036 56529 : for (i=1; i<l; i++) pad[j*l+i] += maxc;
3037 11052 : lgall += maxc + 1; /* column width, including separating space */
3038 11052 : if (lgall > w) { pad = NULL; break; } /* doesn't fit, abort padding */
3039 : }
3040 4094 : set_avma(av2);
3041 : }
3042 16748 : for (i=1; i<l; i++)
3043 : {
3044 12647 : str_putc(S, '[');
3045 65668 : for (j=1; j<r; j++)
3046 : {
3047 53021 : if (pad) {
3048 40005 : long white = pad[j*l+i];
3049 79422 : while (white-- > 0) str_putc(S, ' ');
3050 : }
3051 53021 : print(gcoeff(g,i,j),T,S); if (j<r-1) str_putc(S, ' ');
3052 : }
3053 12647 : if (i<l-1) str_puts(S, "]\n\n"); else str_puts(S, "]\n");
3054 : }
3055 4101 : if (!S->use_stack) set_avma(av);
3056 : }
3057 :
3058 : /********************************************************************/
3059 : /** **/
3060 : /** TeX OUTPUT **/
3061 : /** **/
3062 : /********************************************************************/
3063 : /* this follows bruti_sign */
3064 : static void
3065 516 : texi_sign(GEN g, pariout_t *T, pari_str *S, int addsign)
3066 : {
3067 : long tg,i,j,l,r;
3068 : GEN a,b;
3069 : const char *v;
3070 : char buf[67];
3071 :
3072 516 : if (print_0_or_pm1(g, S, addsign)) return;
3073 :
3074 414 : tg = typ(g);
3075 414 : switch(tg)
3076 : {
3077 137 : case t_INT: case t_REAL: case t_QFB:
3078 137 : bruti_intern(g, T, S, addsign); break;
3079 :
3080 7 : case t_INTMOD: case t_POLMOD:
3081 7 : texi(gel(g,2),T,S); str_puts(S, " mod ");
3082 7 : texi(gel(g,1),T,S); break;
3083 :
3084 11 : case t_FRAC:
3085 11 : if (addsign && isfactor(gel(g,1)) < 0) str_putc(S, '-');
3086 11 : str_puts(S, "\\frac{");
3087 11 : texi_sign(gel(g,1),T,S,0);
3088 11 : str_puts(S, "}{");
3089 11 : texi_sign(gel(g,2),T,S,0);
3090 11 : str_puts(S, "}"); break;
3091 :
3092 14 : case t_RFRAC:
3093 14 : str_puts(S, "\\frac{");
3094 14 : texi(gel(g,1),T,S); /* too complicated otherwise */
3095 14 : str_puts(S, "}{");
3096 14 : texi(gel(g,2),T,S);
3097 14 : str_puts(S, "}"); break;
3098 :
3099 7 : case t_FFELT:
3100 7 : bruti_sign(FF_to_FpXQ_i(g),T,S,addsign);
3101 7 : break;
3102 :
3103 49 : case t_COMPLEX: case t_QUAD: r = (tg==t_QUAD);
3104 49 : v = cxq_init(g, tg, &a, &b, buf);
3105 49 : if (isnull(a))
3106 : {
3107 14 : wr_lead_texnome(T,S,b,v,1,addsign);
3108 14 : break;
3109 : }
3110 35 : texi_sign(a,T,S,addsign);
3111 35 : if (!isnull(b)) wr_texnome(T,S,b,v,1);
3112 35 : break;
3113 :
3114 98 : case t_POL: v = get_texvar(varn(g), buf, sizeof(buf));
3115 : /* hack: we want g[i] = coeff of degree i. */
3116 98 : i = degpol(g); g += 2; while (isnull(gel(g,i))) i--;
3117 98 : wr_lead_texnome(T,S,gel(g,i),v,i,addsign);
3118 294 : while (i--)
3119 : {
3120 196 : a = gel(g,i);
3121 196 : if (!isnull_for_pol(a)) wr_texnome(T,S,a,v,i);
3122 : }
3123 98 : break;
3124 :
3125 7 : case t_SER: v = get_texvar(varn(g), buf, sizeof(buf));
3126 7 : i = valser(g);
3127 7 : if (lg(g)-2)
3128 : { /* hack: we want g[i] = coeff of degree i. */
3129 7 : l = i + lg(g)-2; g -= i-2;
3130 7 : wr_lead_texnome(T,S,gel(g,i),v,i,addsign);
3131 14 : while (++i < l)
3132 : {
3133 7 : a = gel(g,i);
3134 7 : if (!isnull_for_pol(a)) wr_texnome(T,S,a,v,i);
3135 : }
3136 7 : str_puts(S, "+ ");
3137 : }
3138 7 : str_puts(S, "O("); texnome(S,v,i); str_putc(S, ')'); break;
3139 :
3140 7 : case t_PADIC:
3141 : {
3142 7 : GEN p = padic_p(g);
3143 : pari_sp av;
3144 : char *ev;
3145 7 : str_alloc(S, (precp(g)+1) * lgefint(p)); /* careful! */
3146 7 : av = avma;
3147 7 : i = valp(g); l = precp(g)+i;
3148 7 : g = padic_u(g); ev = itostr(p);
3149 21 : for (; i<l; i++)
3150 : {
3151 14 : g = dvmdii(g,p,&a);
3152 14 : if (signe(a))
3153 : {
3154 7 : if (!i || !is_pm1(a))
3155 : {
3156 7 : str_absint(S, a); if (i) str_puts(S, "\\cdot");
3157 : }
3158 7 : if (i) texVpowE(S, ev,i);
3159 7 : str_putc(S, '+');
3160 : }
3161 : }
3162 7 : str_puts(S, "O("); texVpowE(S, ev,i); str_putc(S, ')');
3163 7 : set_avma(av); break;
3164 : }
3165 :
3166 7 : case t_VEC:
3167 7 : str_puts(S, "\\pmatrix{ "); l = lg(g);
3168 21 : for (i=1; i<l; i++)
3169 : {
3170 14 : texi(gel(g,i),T,S); if (i < l-1) str_putc(S, '&');
3171 : }
3172 7 : str_puts(S, "\\cr}\n"); break;
3173 :
3174 14 : case t_LIST:
3175 14 : switch(list_typ(g))
3176 : {
3177 7 : case t_LIST_RAW:
3178 7 : str_puts(S, "\\pmatrix{ ");
3179 7 : g = list_data(g);
3180 7 : l = g? lg(g): 1;
3181 21 : for (i=1; i<l; i++)
3182 : {
3183 14 : texi(gel(g,i),T,S); if (i < l-1) str_putc(S, '&');
3184 : }
3185 7 : str_puts(S, "\\cr}\n"); break;
3186 7 : case t_LIST_MAP:
3187 : {
3188 7 : pari_sp av = avma;
3189 7 : texi(maptomat_shallow(g),T,S);
3190 7 : set_avma(av);
3191 7 : break;
3192 : }
3193 : }
3194 14 : break;
3195 7 : case t_COL:
3196 7 : str_puts(S, "\\pmatrix{ "); l = lg(g);
3197 21 : for (i=1; i<l; i++)
3198 : {
3199 14 : texi(gel(g,i),T,S); str_puts(S, "\\cr\n");
3200 : }
3201 7 : str_putc(S, '}'); break;
3202 :
3203 7 : case t_VECSMALL:
3204 7 : str_puts(S, "\\pmatrix{ "); l = lg(g);
3205 21 : for (i=1; i<l; i++)
3206 : {
3207 14 : str_long(S, g[i]);
3208 14 : if (i < l-1) str_putc(S, '&');
3209 : }
3210 7 : str_puts(S, "\\cr}\n"); break;
3211 :
3212 0 : case t_STR:
3213 0 : str_puts(S, GSTR(g)); break;
3214 :
3215 7 : case t_CLOSURE:
3216 7 : if (lg(g)>=6)
3217 : {
3218 7 : GEN str = closure_get_text(g);
3219 7 : if (typ(str)==t_STR)
3220 : {
3221 0 : print_precontext(g, S, 1);
3222 0 : str_puts(S, GSTR(str));
3223 0 : print_context(g, T, S, 1);
3224 : }
3225 : else
3226 : {
3227 7 : str_putc(S,'('); str_puts(S,GSTR(gel(str,1)));
3228 7 : str_puts(S,")\\mapsto ");
3229 7 : print_context(g, T, S, 1); str_puts(S,GSTR(gel(str,2)));
3230 : }
3231 : }
3232 : else
3233 : {
3234 0 : str_puts(S,"\\{\""); str_puts(S,GSTR(closure_get_code(g)));
3235 0 : str_puts(S,"\","); texi(gel(g,3),T,S);
3236 0 : str_putc(S,','); texi(gel(g,4),T,S);
3237 0 : str_putc(S,','); texi(gel(g,5),T,S); str_puts(S,"\\}");
3238 : }
3239 7 : break;
3240 14 : case t_INFINITY: str_puts(S, inf_get_sign(g) == 1? "+\\infty": "-\\infty");
3241 14 : break;
3242 :
3243 21 : case t_MAT:
3244 : {
3245 21 : str_puts(S, "\\pmatrix{\n "); r = lg(g);
3246 21 : if (r>1)
3247 : {
3248 21 : OUT_FUN print = (typ(gel(g,1)) == t_VECSMALL)? prints: texi;
3249 :
3250 21 : l = lgcols(g);
3251 56 : for (i=1; i<l; i++)
3252 : {
3253 98 : for (j=1; j<r; j++)
3254 : {
3255 63 : print(gcoeff(g,i,j),T,S); if (j<r-1) str_putc(S, '&');
3256 : }
3257 35 : str_puts(S, "\\cr\n ");
3258 : }
3259 : }
3260 21 : str_putc(S, '}'); break;
3261 : }
3262 : }
3263 : }
3264 :
3265 : /*******************************************************************/
3266 : /** **/
3267 : /** USER OUTPUT FUNCTIONS **/
3268 : /** **/
3269 : /*******************************************************************/
3270 : static void
3271 0 : _initout(pariout_t *T, char f, long sigd, long sp)
3272 : {
3273 0 : T->format = f;
3274 0 : T->sigd = sigd;
3275 0 : T->sp = sp;
3276 0 : }
3277 :
3278 : static void
3279 61009 : gen_output_fun(GEN x, pariout_t *T, OUT_FUN out)
3280 61009 : { pari_sp av = avma; pari_puts( stack_GENtostr_fun(x,T,out) ); set_avma(av); }
3281 :
3282 : void
3283 0 : fputGEN_pariout(GEN x, pariout_t *T, FILE *out)
3284 : {
3285 0 : pari_sp av = avma;
3286 0 : char *s = stack_GENtostr_fun(x, T, get_fun(T->prettyp));
3287 0 : if (*s) { set_last_newline(s[strlen(s)-1]); fputs(s, out); }
3288 0 : set_avma(av);
3289 0 : }
3290 :
3291 : void
3292 0 : brute(GEN g, char f, long d)
3293 : {
3294 0 : pariout_t T; _initout(&T,f,d,0);
3295 0 : gen_output_fun(g, &T, &bruti);
3296 0 : }
3297 : void
3298 0 : matbrute(GEN g, char f, long d)
3299 : {
3300 0 : pariout_t T; _initout(&T,f,d,1);
3301 0 : gen_output_fun(g, &T, &matbruti);
3302 0 : }
3303 : void
3304 0 : texe(GEN g, char f, long d)
3305 : {
3306 0 : pariout_t T; _initout(&T,f,d,0);
3307 0 : gen_output_fun(g, &T, &texi);
3308 0 : }
3309 :
3310 : void
3311 61009 : gen_output(GEN x)
3312 : {
3313 61009 : gen_output_fun(x, GP_DATA->fmt, get_fun(GP_DATA->fmt->prettyp));
3314 61009 : pari_putc('\n'); pari_flush();
3315 61009 : }
3316 : void
3317 0 : output(GEN x)
3318 0 : { brute(x,'g',-1); pari_putc('\n'); pari_flush(); }
3319 : void
3320 0 : outmat(GEN x)
3321 0 : { matbrute(x,'g',-1); pari_putc('\n'); pari_flush(); }
3322 :
3323 : /*******************************************************************/
3324 : /** FILES **/
3325 : /*******************************************************************/
3326 : /* to cache '~' expansion */
3327 : static char *homedir;
3328 : /* last file read successfully from try_name() */
3329 : static THREAD char *last_filename;
3330 : /* stack of temporary files (includes all infiles + some output) */
3331 : static THREAD pariFILE *last_tmp_file;
3332 : /* stack of "permanent" (output) files */
3333 : static THREAD pariFILE *last_file;
3334 :
3335 : typedef struct gpfile
3336 : {
3337 : const char *name;
3338 : FILE *fp;
3339 : int type;
3340 : long serial;
3341 : } gpfile;
3342 :
3343 : static THREAD gpfile *gp_file;
3344 : static THREAD pari_stack s_gp_file;
3345 : static THREAD long gp_file_serial;
3346 :
3347 : #if defined(UNIX) || defined(__EMX__)
3348 : # include <fcntl.h>
3349 : # include <sys/stat.h> /* for open */
3350 : # ifdef __EMX__
3351 : # include <process.h>
3352 : # endif
3353 : # define HAVE_PIPES
3354 : #endif
3355 : #if defined(_WIN32)
3356 : # define HAVE_PIPES
3357 : #endif
3358 : #ifndef O_RDONLY
3359 : # define O_RDONLY 0
3360 : #endif
3361 :
3362 : pariFILE *
3363 40460 : newfile(FILE *f, const char *name, int type)
3364 : {
3365 40460 : pariFILE *file = (pariFILE*) pari_malloc(strlen(name) + 1 + sizeof(pariFILE));
3366 40460 : file->type = type;
3367 40460 : file->name = strcpy((char*)(file+1), name);
3368 40460 : file->file = f;
3369 40460 : file->next = NULL;
3370 40460 : if (type & mf_PERM)
3371 : {
3372 0 : file->prev = last_file;
3373 0 : last_file = file;
3374 : }
3375 : else
3376 : {
3377 40460 : file->prev = last_tmp_file;
3378 40460 : last_tmp_file = file;
3379 : }
3380 40460 : if (file->prev) (file->prev)->next = file;
3381 40460 : if (DEBUGLEVEL)
3382 0 : if (strcmp(name,"stdin") || DEBUGLEVEL > 9)
3383 0 : err_printf("I/O: new pariFILE %s (code %d) \n",name,type);
3384 40460 : return file;
3385 : }
3386 :
3387 : static void
3388 40460 : pari_kill_file(pariFILE *f)
3389 : {
3390 40460 : if ((f->type & mf_PIPE) == 0)
3391 : {
3392 40452 : if (f->file != stdin && fclose(f->file))
3393 0 : pari_warn(warnfile, "close", f->name);
3394 : }
3395 : #ifdef HAVE_PIPES
3396 : else
3397 : {
3398 8 : if (f->type & mf_FALSE)
3399 : {
3400 0 : if (f->file != stdin && fclose(f->file))
3401 0 : pari_warn(warnfile, "close", f->name);
3402 0 : if (unlink(f->name)) pari_warn(warnfile, "delete", f->name);
3403 : }
3404 : else
3405 8 : if (pclose(f->file) < 0) pari_warn(warnfile, "close pipe", f->name);
3406 : }
3407 : #endif
3408 40460 : if (DEBUGLEVEL)
3409 0 : if (strcmp(f->name,"stdin") || DEBUGLEVEL > 9)
3410 0 : err_printf("I/O: closing file %s (code %d) \n",f->name,f->type);
3411 40460 : pari_free(f);
3412 40460 : }
3413 :
3414 : void
3415 40383 : pari_fclose(pariFILE *f)
3416 : {
3417 40383 : if (f->next) (f->next)->prev = f->prev;
3418 40383 : else if (f == last_tmp_file) last_tmp_file = f->prev;
3419 0 : else if (f == last_file) last_file = f->prev;
3420 40383 : if (f->prev) (f->prev)->next = f->next;
3421 40383 : pari_kill_file(f);
3422 40383 : }
3423 :
3424 : static pariFILE *
3425 0 : pari_open_file(FILE *f, const char *s, const char *mode)
3426 : {
3427 0 : if (!f) pari_err_FILE("requested file", s);
3428 0 : if (DEBUGLEVEL)
3429 0 : if (strcmp(s,"stdin") || DEBUGLEVEL > 9)
3430 0 : err_printf("I/O: opening file %s (mode %s)\n", s, mode);
3431 0 : return newfile(f,s,0);
3432 : }
3433 :
3434 : pariFILE *
3435 0 : pari_fopen_or_fail(const char *s, const char *mode)
3436 : {
3437 0 : return pari_open_file(fopen(s, mode), s, mode);
3438 : }
3439 : pariFILE *
3440 0 : pari_fopen(const char *s, const char *mode)
3441 : {
3442 0 : FILE *f = fopen(s, mode);
3443 0 : return f? pari_open_file(f, s, mode): NULL;
3444 : }
3445 :
3446 : void
3447 112212 : pari_fread_chars(void *b, size_t n, FILE *f)
3448 : {
3449 112212 : if (fread(b, sizeof(char), n, f) < n)
3450 0 : pari_err_FILE("input file [fread]", "FILE*");
3451 112212 : }
3452 :
3453 : /* FIXME: HAS_FDOPEN & allow standard open() flags */
3454 : #ifdef UNIX
3455 : /* open tmpfile s (a priori for writing) avoiding symlink attacks */
3456 : pariFILE *
3457 0 : pari_safefopen(const char *s, const char *mode)
3458 : {
3459 0 : long fd = open(s, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
3460 :
3461 0 : if (fd == -1) pari_err(e_MISC,"tempfile %s already exists",s);
3462 0 : return pari_open_file(fdopen(fd, mode), s, mode);
3463 : }
3464 : #else
3465 : pariFILE *
3466 : pari_safefopen(const char *s, const char *mode)
3467 : {
3468 : return pari_fopen_or_fail(s, mode);
3469 : }
3470 : #endif
3471 :
3472 : void
3473 0 : pari_unlink(const char *s)
3474 : {
3475 0 : if (unlink(s)) pari_warn(warner, "I/O: can\'t remove file %s", s);
3476 0 : else if (DEBUGLEVEL)
3477 0 : err_printf("I/O: removed file %s\n", s);
3478 0 : }
3479 :
3480 : /* Remove one INFILE from the stack. Reset pari_infile (to the most recent
3481 : * infile)
3482 : * Return -1, if we're trying to pop out stdin itself; 0 otherwise
3483 : * Check for leaked file handlers (temporary files) */
3484 : int
3485 380447 : popinfile(void)
3486 : {
3487 380447 : pariFILE *f = last_tmp_file, *g;
3488 380447 : while (f)
3489 : {
3490 20 : if (f->type & mf_IN) break;
3491 0 : pari_warn(warner, "I/O: leaked file descriptor (%d): %s", f->type, f->name);
3492 0 : g = f; f = f->prev; pari_fclose(g);
3493 : }
3494 380447 : last_tmp_file = f; if (!f) return -1;
3495 20 : pari_fclose(last_tmp_file);
3496 20 : for (f = last_tmp_file; f; f = f->prev)
3497 0 : if (f->type & mf_IN) { pari_infile = f->file; return 0; }
3498 20 : pari_infile = stdin; return 0;
3499 : }
3500 :
3501 : /* delete all "temp" files open since last reference point F */
3502 : void
3503 13617 : tmp_restore(pariFILE *F)
3504 : {
3505 13617 : pariFILE *f = last_tmp_file;
3506 13617 : int first = 1;
3507 13631 : while (f)
3508 : {
3509 35 : pariFILE *g = f->prev;
3510 35 : if (f == F) break;
3511 14 : pari_fclose(f); f = g;
3512 : }
3513 13617 : for (; f; f = f->prev) {
3514 21 : if (f->type & mf_IN) {
3515 21 : pari_infile = f->file;
3516 21 : if (DEBUGLEVEL>1)
3517 : {
3518 0 : first = 0;
3519 0 : err_printf("restoring pari_infile to %s\n", f->name);
3520 : }
3521 21 : break;
3522 : }
3523 : }
3524 13617 : if (!f) {
3525 13596 : pari_infile = stdin;
3526 13596 : if (DEBUGLEVEL>1 && (!first || DEBUGLEVEL > 9))
3527 : {
3528 7 : first = 0;
3529 7 : err_printf("gp_context_restore: restoring pari_infile to stdin\n");
3530 : }
3531 : }
3532 13617 : if (!first && DEBUGLEVEL>1) err_printf("done\n");
3533 13617 : }
3534 :
3535 : void
3536 146779 : filestate_save(struct pari_filestate *file)
3537 : {
3538 146779 : file->file = last_tmp_file;
3539 146779 : file->serial = gp_file_serial;
3540 146779 : }
3541 :
3542 : static void
3543 391705 : filestate_close(long serial)
3544 : {
3545 : long i;
3546 391721 : for (i = 0; i < s_gp_file.n; i++)
3547 16 : if (gp_file[i].fp && gp_file[i].serial >= serial)
3548 16 : gp_fileclose(i);
3549 391705 : gp_file_serial = serial;
3550 391705 : }
3551 :
3552 : void
3553 13165 : filestate_restore(struct pari_filestate *file)
3554 : {
3555 13165 : tmp_restore(file->file);
3556 13165 : filestate_close(file->serial);
3557 13165 : }
3558 :
3559 : static void
3560 757129 : kill_file_stack(pariFILE **s)
3561 : {
3562 757129 : pariFILE *f = *s;
3563 757206 : while (f)
3564 : {
3565 77 : pariFILE *t = f->prev;
3566 77 : pari_kill_file(f);
3567 77 : *s = f = t; /* have to update *s in case of ^C */
3568 : }
3569 757129 : }
3570 :
3571 : void
3572 49 : killallfiles(void)
3573 : {
3574 49 : kill_file_stack(&last_tmp_file);
3575 49 : pari_infile = stdin;
3576 49 : }
3577 :
3578 : void
3579 1895 : pari_init_homedir(void)
3580 : {
3581 1895 : homedir = NULL;
3582 1895 : }
3583 :
3584 : void
3585 1887 : pari_close_homedir(void)
3586 : {
3587 1887 : if (homedir) pari_free(homedir);
3588 1887 : }
3589 :
3590 : void
3591 378548 : pari_init_files(void)
3592 : {
3593 378548 : last_filename = NULL;
3594 378548 : last_tmp_file = NULL;
3595 378548 : last_file=NULL;
3596 378548 : pari_stack_init(&s_gp_file, sizeof(*gp_file), (void**)&gp_file);
3597 378548 : gp_file_serial = 0;
3598 378548 : }
3599 :
3600 : void
3601 378540 : pari_thread_close_files(void)
3602 : {
3603 378540 : popinfile(); /* look for leaks */
3604 378540 : kill_file_stack(&last_file);
3605 378540 : if (last_filename) pari_free(last_filename);
3606 378540 : kill_file_stack(&last_tmp_file);
3607 378540 : filestate_close(-1);
3608 378540 : pari_stack_delete(&s_gp_file);
3609 378540 : }
3610 :
3611 : void
3612 1887 : pari_close_files(void)
3613 : {
3614 1887 : if (pari_logfile) { fclose(pari_logfile); pari_logfile = NULL; }
3615 1887 : pari_infile = stdin;
3616 1887 : }
3617 :
3618 : static int
3619 0 : ok_pipe(FILE *f)
3620 : {
3621 0 : if (DEBUGLEVEL) err_printf("I/O: checking output pipe...\n");
3622 0 : pari_CATCH(CATCH_ALL) {
3623 0 : return 0;
3624 : }
3625 : pari_TRY {
3626 : int i;
3627 0 : fprintf(f,"\n\n"); fflush(f);
3628 0 : for (i=1; i<1000; i++) fprintf(f," \n");
3629 0 : fprintf(f,"\n"); fflush(f);
3630 0 : } pari_ENDCATCH;
3631 0 : return 1;
3632 : }
3633 :
3634 : pariFILE *
3635 8 : try_pipe(const char *cmd, int fl)
3636 : {
3637 : #ifndef HAVE_PIPES
3638 : pari_err(e_ARCH,"pipes");
3639 : return NULL;/*LCOV_EXCL_LINE*/
3640 : #else
3641 : FILE *file;
3642 : const char *f;
3643 8 : VOLATILE int flag = fl;
3644 :
3645 : # ifdef __EMX__
3646 : if (_osmode == DOS_MODE) /* no pipes under DOS */
3647 : {
3648 : pari_sp av = avma;
3649 : char *s;
3650 : if (flag & mf_OUT) pari_err(e_ARCH,"pipes");
3651 : f = pari_unique_filename("pipe");
3652 : s = stack_malloc(strlen(cmd)+strlen(f)+4);
3653 : sprintf(s,"%s > %s",cmd,f);
3654 : file = system(s)? NULL: fopen(f,"r");
3655 : flag |= mf_FALSE; pari_free(f); set_avma(av);
3656 : }
3657 : else
3658 : # endif
3659 : {
3660 8 : file = (FILE *) popen(cmd, (flag & mf_OUT)? "w": "r");
3661 8 : if (flag & mf_OUT) {
3662 0 : if (!ok_pipe(file)) return NULL;
3663 0 : flag |= mf_PERM;
3664 : }
3665 8 : f = cmd;
3666 : }
3667 8 : if (!file) pari_err(e_MISC,"[pipe:] '%s' failed",cmd);
3668 8 : return newfile(file, f, mf_PIPE|flag);
3669 : #endif
3670 : }
3671 :
3672 : char *
3673 26762 : os_getenv(const char *s)
3674 : {
3675 : #ifdef HAS_GETENV
3676 26762 : return getenv(s);
3677 : #else
3678 : (void) s; return NULL;
3679 : #endif
3680 : }
3681 :
3682 : GEN
3683 0 : gp_getenv(const char *s)
3684 : {
3685 0 : char *t = os_getenv(s);
3686 0 : return t?strtoGENstr(t):gen_0;
3687 : }
3688 :
3689 : /* FIXME: HAS_GETPWUID */
3690 : #if defined(UNIX) || defined(__EMX__)
3691 : #include <pwd.h>
3692 : #include <sys/types.h>
3693 : /* user = "": use current uid */
3694 : char *
3695 3778 : pari_get_homedir(const char *user)
3696 : {
3697 : struct passwd *p;
3698 3778 : char *dir = NULL;
3699 :
3700 3778 : if (!*user)
3701 : {
3702 3778 : if (homedir) dir = homedir;
3703 : else
3704 : {
3705 1889 : p = getpwuid(geteuid());
3706 1889 : if (p)
3707 : {
3708 1889 : dir = p->pw_dir;
3709 1889 : homedir = pari_strdup(dir); /* cache result */
3710 : }
3711 : }
3712 : }
3713 : else
3714 : {
3715 0 : p = getpwnam(user);
3716 0 : if (p) dir = p->pw_dir;
3717 : /* warn, but don't kill session on startup (when expanding path) */
3718 0 : if (!dir) pari_warn(warner,"can't expand ~%s", user? user: "");
3719 : }
3720 3778 : return dir;
3721 : }
3722 : #else
3723 : char *
3724 : pari_get_homedir(const char *user) { (void) user; return NULL; }
3725 : #endif
3726 :
3727 : /*******************************************************************/
3728 : /** **/
3729 : /** GP STANDARD INPUT AND OUTPUT **/
3730 : /** **/
3731 : /*******************************************************************/
3732 : #ifdef HAS_STAT
3733 : static int
3734 55 : is_dir_stat(const char *name)
3735 : {
3736 : struct stat buf;
3737 55 : if (stat(name, &buf)) return 0;
3738 55 : return S_ISDIR(buf.st_mode);
3739 : }
3740 : #elif defined(HAS_OPENDIR)
3741 : /* slow, but more portable than stat + S_ISDIR */
3742 : static int
3743 : is_dir_opendir(const char *name)
3744 : {
3745 : DIR *d = opendir(name);
3746 : if (d) { (void)closedir(d); return 1; }
3747 : return 0;
3748 : }
3749 : #endif
3750 :
3751 :
3752 : /* Does name point to a directory? */
3753 : int
3754 55 : pari_is_dir(const char *name)
3755 : {
3756 : #ifdef HAS_STAT
3757 55 : return is_dir_stat(name);
3758 : #elif defined(HAS_OPENDIR)
3759 : return is_dir_opendir(name);
3760 : #else
3761 : (void) name; return 0;
3762 : #endif
3763 : }
3764 :
3765 : /* Does name point to a regular file? */
3766 : /* If unknown, assume that it is indeed regular. */
3767 : int
3768 94 : pari_is_file(const char *name)
3769 : {
3770 : #ifdef HAS_STAT
3771 : struct stat buf;
3772 94 : if (stat(name, &buf)) return 1;
3773 67 : return S_ISREG(buf.st_mode);
3774 : #else
3775 : (void) name; return 1;
3776 : #endif
3777 : }
3778 :
3779 : int
3780 1895 : pari_stdin_isatty(void)
3781 : {
3782 : #ifdef HAS_ISATTY
3783 1895 : return isatty( fileno(stdin) );
3784 : #else
3785 : return 1;
3786 : #endif
3787 : }
3788 :
3789 : /* expand tildes in filenames, return a malloc'ed buffer */
3790 : static char *
3791 5798 : _path_expand(const char *s)
3792 : {
3793 : const char *t;
3794 5798 : char *ret, *dir = NULL;
3795 :
3796 5798 : if (*s != '~') return pari_strdup(s);
3797 3778 : s++; /* skip ~ */
3798 3778 : t = s; while (*t && *t != '/') t++;
3799 3778 : if (t == s)
3800 3778 : dir = pari_get_homedir("");
3801 : else
3802 : {
3803 0 : char *user = pari_strndup(s, t - s);
3804 0 : dir = pari_get_homedir(user);
3805 0 : pari_free(user);
3806 : }
3807 3778 : if (!dir) return pari_strdup(s);
3808 3778 : ret = (char*)pari_malloc(strlen(dir) + strlen(t) + 1);
3809 3778 : sprintf(ret,"%s%s",dir,t); return ret;
3810 : }
3811 :
3812 : /* expand environment variables in str, return a malloc'ed buffer
3813 : * assume no \ remain and str can be freed */
3814 : static char *
3815 5798 : _expand_env(char *str)
3816 : {
3817 5798 : long i, l, len = 0, xlen = 16, xnum = 0;
3818 5798 : char *s = str, *s0 = s;
3819 5798 : char **x = (char **)pari_malloc(xlen * sizeof(char*));
3820 :
3821 44881 : while (*s)
3822 : {
3823 : char *env;
3824 39083 : if (*s != '$') { s++; continue; }
3825 0 : l = s - s0;
3826 0 : if (l) { x[xnum++] = pari_strndup(s0, l); len += l; }
3827 0 : if (xnum > xlen - 3) /* need room for possibly two more elts */
3828 : {
3829 0 : xlen <<= 1;
3830 0 : pari_realloc_ip((void**)&x, xlen * sizeof(char*));
3831 : }
3832 :
3833 0 : s0 = ++s; /* skip $ */
3834 0 : while (is_keyword_char(*s)) s++;
3835 0 : l = s - s0; env = pari_strndup(s0, l);
3836 0 : s0 = os_getenv(env);
3837 0 : if (!s0) pari_warn(warner,"undefined environment variable: %s",env);
3838 : else
3839 : {
3840 0 : l = strlen(s0);
3841 0 : if (l) { x[xnum++] = pari_strndup(s0,l); len += l; }
3842 : }
3843 0 : pari_free(env); s0 = s;
3844 : }
3845 5798 : l = s - s0;
3846 5798 : if (l) { x[xnum++] = pari_strndup(s0,l); len += l; }
3847 :
3848 5798 : s = (char*)pari_malloc(len+1); *s = 0;
3849 11596 : for (i = 0; i < xnum; i++) { (void)strcat(s, x[i]); pari_free(x[i]); }
3850 5798 : pari_free(str); pari_free(x); return s;
3851 : }
3852 :
3853 : char *
3854 5798 : path_expand(const char *s)
3855 : {
3856 : #ifdef _WIN32
3857 : char *ss, *p;
3858 : ss = pari_strdup(s);
3859 : for (p = ss; *p != 0; ++p)
3860 : if (*p == '\\') *p = '/';
3861 : p = _expand_env(_path_expand(ss));
3862 : pari_free(ss);
3863 : return p;
3864 : #else
3865 5798 : return _expand_env(_path_expand(s));
3866 : #endif
3867 : }
3868 :
3869 : #ifdef HAS_STRFTIME
3870 : # include <time.h>
3871 : void
3872 4 : strftime_expand(const char *s, char *buf, long max)
3873 : {
3874 : time_t t;
3875 4 : BLOCK_SIGINT_START
3876 4 : t = time(NULL);
3877 4 : (void)strftime(buf,max,s,localtime(&t));
3878 4 : BLOCK_SIGINT_END
3879 4 : }
3880 : #else
3881 : void
3882 : strftime_expand(const char *s, char *buf, long max)
3883 : { strcpy(buf,s); }
3884 : #endif
3885 :
3886 : /* name is a malloc'ed (existing) filename. Accept it as new pari_infile
3887 : * (unzip if needed). */
3888 : static pariFILE *
3889 40361 : pari_get_infile(const char *name, FILE *file)
3890 : {
3891 : #ifdef ZCAT
3892 40361 : long l = strlen(name);
3893 40361 : const char *end = name + l-1;
3894 :
3895 40361 : if (l > 2 && (!strncmp(end-1,".Z",2)
3896 : #ifdef GNUZCAT
3897 40361 : || !strncmp(end-2,".gz",3)
3898 : #endif
3899 : ))
3900 : { /* compressed file (compress or gzip) */
3901 0 : char *cmd = stack_malloc(strlen(ZCAT) + l + 4);
3902 0 : sprintf(cmd,"%s \"%s\"",ZCAT,name);
3903 0 : fclose(file);
3904 0 : return try_pipe(cmd, mf_IN);
3905 : }
3906 : #endif
3907 40361 : return newfile(file, name, mf_IN);
3908 : }
3909 :
3910 : pariFILE *
3911 40404 : pari_fopengz(const char *s)
3912 : {
3913 40404 : pari_sp av = avma;
3914 : char *name;
3915 : long l;
3916 40404 : FILE *f = fopen(s, "r");
3917 : pariFILE *pf;
3918 :
3919 40404 : if (f) return pari_get_infile(s, f);
3920 :
3921 : #ifdef __EMSCRIPTEN__
3922 : if (pari_is_dir(pari_datadir))
3923 : {
3924 : pari_emscripten_wget(s);
3925 : f = fopen(s, "r");
3926 : if (f) return pari_get_infile(s, f);
3927 : }
3928 : #endif
3929 63 : l = strlen(s);
3930 63 : name = stack_malloc(l + 3 + 1);
3931 63 : strcpy(name, s); (void)sprintf(name + l, ".gz");
3932 63 : f = fopen(name, "r");
3933 63 : pf = f ? pari_get_infile(name, f): NULL;
3934 63 : set_avma(av); return pf;
3935 : }
3936 :
3937 : static FILE*
3938 20 : try_open(char *s)
3939 : {
3940 20 : if (!pari_is_dir(s)) return fopen(s, "r");
3941 0 : pari_warn(warner,"skipping directory %s",s);
3942 0 : return NULL;
3943 : }
3944 :
3945 : void
3946 20 : forpath_init(forpath_t *T, gp_path *path, const char *s)
3947 : {
3948 20 : T->s = s;
3949 20 : T->ls = strlen(s);
3950 20 : T->dir = path->dirs;
3951 20 : }
3952 : char *
3953 20 : forpath_next(forpath_t *T)
3954 : {
3955 20 : char *t, *dir = T->dir[0];
3956 :
3957 20 : if (!dir) return NULL; /* done */
3958 : /* room for dir + '/' + s + '\0' */
3959 20 : t = (char*)pari_malloc(strlen(dir) + T->ls + 2);
3960 20 : if (!t) return NULL; /* can't happen but kills a warning */
3961 20 : sprintf(t,"%s/%s", dir, T->s);
3962 20 : T->dir++; return t;
3963 : }
3964 :
3965 : /* If a file called "name" exists (possibly after appending ".gp")
3966 : * record it in the file_stack (as a pipe if compressed).
3967 : * name is malloc'ed, we free it before returning
3968 : */
3969 : static FILE *
3970 20 : try_name(char *name)
3971 : {
3972 20 : pari_sp av = avma;
3973 20 : char *s = name;
3974 20 : FILE *file = try_open(name);
3975 :
3976 20 : if (!file)
3977 : { /* try appending ".gp" to name */
3978 0 : s = stack_malloc(strlen(name)+4);
3979 0 : sprintf(s, "%s.gp", name);
3980 0 : file = try_open(s);
3981 : }
3982 20 : if (file)
3983 : {
3984 20 : if (! last_tmp_file)
3985 : { /* empty file stack, record this name */
3986 20 : if (last_filename) pari_free(last_filename);
3987 20 : last_filename = pari_strdup(s);
3988 : }
3989 20 : file = pari_infile = pari_get_infile(s,file)->file;
3990 : }
3991 20 : pari_free(name); set_avma(av);
3992 20 : return file;
3993 : }
3994 : static FILE *
3995 7 : switchin_last(void)
3996 : {
3997 7 : char *s = last_filename;
3998 : FILE *file;
3999 7 : if (!s) pari_err(e_MISC,"You never gave me anything to read!");
4000 0 : file = try_open(s);
4001 0 : if (!file) pari_err_FILE("input file",s);
4002 0 : return pari_infile = pari_get_infile(s,file)->file;
4003 : }
4004 :
4005 : /* return 1 if s starts by '/' or './' or '../' */
4006 : static int
4007 20 : path_is_absolute(char *s)
4008 : {
4009 : #ifdef _WIN32
4010 : if( (*s >= 'A' && *s <= 'Z') ||
4011 : (*s >= 'a' && *s <= 'z') )
4012 : {
4013 : return *(s+1) == ':';
4014 : }
4015 : #endif
4016 20 : if (*s == '/') return 1;
4017 20 : if (*s++ != '.') return 0;
4018 0 : if (*s == '/') return 1;
4019 0 : if (*s++ != '.') return 0;
4020 0 : return *s == '/';
4021 : }
4022 :
4023 : /* If name = "", re-read last file */
4024 : FILE *
4025 27 : switchin(const char *name)
4026 : {
4027 : FILE *f;
4028 : char *s;
4029 :
4030 27 : if (!*name) return switchin_last();
4031 20 : s = path_expand(name);
4032 : /* if s is an absolute path, don't use dir_list */
4033 20 : if (path_is_absolute(s)) { if ((f = try_name(s))) return f; }
4034 : else
4035 : {
4036 : char *t;
4037 : forpath_t T;
4038 20 : forpath_init(&T, GP_DATA->path, s);
4039 20 : while ( (t = forpath_next(&T)) )
4040 20 : if ((f = try_name(t))) { pari_free(s); return f; }
4041 0 : pari_free(s);
4042 : }
4043 0 : pari_err_FILE("input file",name);
4044 : return NULL; /*LCOV_EXCL_LINE*/
4045 : }
4046 :
4047 : static int is_magic_ok(FILE *f);
4048 :
4049 : static FILE *
4050 94 : switchout_get_FILE(const char *name)
4051 : {
4052 : FILE* f;
4053 : /* only for ordinary files (to avoid blocking on pipes). */
4054 94 : if (pari_is_file(name))
4055 : {
4056 94 : f = fopen(name, "r");
4057 94 : if (f)
4058 : {
4059 67 : int magic = is_magic_ok(f);
4060 67 : fclose(f);
4061 67 : if (magic) pari_err_FILE("binary output file [ use writebin ! ]", name);
4062 : }
4063 : }
4064 94 : f = fopen(name, "a");
4065 94 : if (!f) pari_err_FILE("output file",name);
4066 94 : return f;
4067 : }
4068 :
4069 : void
4070 0 : switchout(const char *name)
4071 : {
4072 0 : if (name)
4073 0 : pari_outfile = switchout_get_FILE(name);
4074 0 : else if (pari_outfile != stdout)
4075 : {
4076 0 : fclose(pari_outfile);
4077 0 : pari_outfile = stdout;
4078 : }
4079 0 : }
4080 :
4081 : /*******************************************************************/
4082 : /** **/
4083 : /** SYSTEM, READSTR/EXTERNSTR/EXTERN **/
4084 : /** **/
4085 : /*******************************************************************/
4086 : static void
4087 40 : check_secure(const char *s)
4088 : {
4089 40 : if (GP_DATA->secure)
4090 0 : pari_err(e_MISC, "[secure mode]: system commands not allowed\nTried to run '%s'",s);
4091 40 : }
4092 :
4093 : long
4094 28 : gpsystem(const char *s)
4095 : {
4096 28 : int x = -1;
4097 : #ifdef HAS_SYSTEM
4098 28 : check_secure(s);
4099 28 : x = system(s);
4100 28 : if (x < 0) pari_err(e_MISC, "system(\"%s\") failed", s);
4101 : #if (defined(WIFEXITED)&&defined(WEXITSTATUS))
4102 28 : x = WIFEXITED(x)? WEXITSTATUS(x): -1; /* POSIX */
4103 : # endif
4104 : #else
4105 : pari_err(e_ARCH,"system");
4106 : #endif
4107 28 : return (long)x;
4108 : }
4109 :
4110 : static GEN
4111 8 : get_lines(FILE *F)
4112 : {
4113 8 : pari_sp av = avma;
4114 8 : long i, nz = 16;
4115 8 : GEN z = cgetg(nz + 1, t_VEC);
4116 8 : Buffer *b = new_buffer();
4117 : input_method IM;
4118 8 : IM.myfgets = (fgets_t)&fgets;
4119 8 : IM.file = (void*)F;
4120 8 : for(i = 1;;)
4121 20 : {
4122 28 : char *s = b->buf, *e;
4123 28 : if (!file_getline(b, &s, &IM)) break;
4124 20 : if (i > nz) { nz <<= 1; z = vec_lengthen(z, nz); }
4125 20 : e = s + strlen(s)-1;
4126 20 : if (*e == '\n') *e = 0;
4127 20 : gel(z,i++) = strtoGENstr(s);
4128 : }
4129 8 : delete_buffer(b); setlg(z, i);
4130 8 : return gc_GEN(av, z);
4131 : }
4132 :
4133 : GEN
4134 4 : externstr(const char *s)
4135 : {
4136 : pariFILE *F;
4137 : GEN z;
4138 4 : check_secure(s);
4139 4 : F = try_pipe(s, mf_IN);
4140 4 : z = get_lines(F->file);
4141 4 : pari_fclose(F); return z;
4142 : }
4143 : GEN
4144 4 : gpextern(const char *s)
4145 : {
4146 : pariFILE *F;
4147 : GEN z;
4148 4 : check_secure(s);
4149 4 : F = try_pipe(s, mf_IN);
4150 4 : z = gp_read_stream(F->file);
4151 4 : pari_fclose(F); return z ? z : gnil;
4152 : }
4153 :
4154 : GEN
4155 4 : readstr(const char *s)
4156 : {
4157 4 : GEN z = get_lines(switchin(s));
4158 4 : popinfile(); return z;
4159 : }
4160 :
4161 : /*******************************************************************/
4162 : /** **/
4163 : /** I/O IN BINARY FORM **/
4164 : /** **/
4165 : /*******************************************************************/
4166 : static void
4167 112 : pari_fread_longs(void *a, size_t c, FILE *d)
4168 112 : { if (fread(a,sizeof(long),c,d) < c)
4169 0 : pari_err_FILE("input file [fread]", "FILE*"); }
4170 :
4171 : static void
4172 152 : _fwrite(const void *a, size_t b, size_t c, FILE *d)
4173 152 : { if (fwrite(a,b,c,d) < c) pari_err_FILE("output file [fwrite]", "FILE*"); }
4174 : static void
4175 136 : _lfwrite(const void *a, size_t b, FILE *c) { _fwrite(a,sizeof(long),b,c); }
4176 : static void
4177 16 : _cfwrite(const void *a, size_t b, FILE *c) { _fwrite(a,sizeof(char),b,c); }
4178 :
4179 : enum { BIN_GEN, NAM_GEN, VAR_GEN, RELINK_TABLE };
4180 :
4181 : static long
4182 88 : rd_long(FILE *f) { long L; pari_fread_longs(&L, 1UL, f); return L; }
4183 : static void
4184 112 : wr_long(long L, FILE *f) { _lfwrite(&L, 1UL, f); }
4185 :
4186 : /* append x to file f */
4187 : static void
4188 24 : wrGEN(GEN x, FILE *f)
4189 : {
4190 24 : GENbin *p = copy_bin_canon(x);
4191 24 : size_t L = p->len;
4192 :
4193 24 : wr_long(L,f);
4194 24 : if (L)
4195 : {
4196 24 : wr_long((long)p->x,f);
4197 24 : wr_long((long)p->base,f);
4198 24 : _lfwrite(GENbinbase(p), L,f);
4199 : }
4200 24 : pari_free((void*)p);
4201 24 : }
4202 :
4203 : static void
4204 16 : wrstr(const char *s, FILE *f)
4205 : {
4206 16 : size_t L = strlen(s)+1;
4207 16 : wr_long(L,f);
4208 16 : _cfwrite(s, L, f);
4209 16 : }
4210 :
4211 : static char *
4212 16 : rdstr(FILE *f)
4213 : {
4214 16 : size_t L = (size_t)rd_long(f);
4215 : char *s;
4216 16 : if (!L) return NULL;
4217 16 : s = (char*)pari_malloc(L);
4218 16 : pari_fread_chars(s, L, f); return s;
4219 : }
4220 :
4221 : static void
4222 8 : writeGEN(GEN x, FILE *f)
4223 : {
4224 8 : fputc(BIN_GEN,f);
4225 8 : wrGEN(x, f);
4226 8 : }
4227 :
4228 : static void
4229 16 : writenamedGEN(GEN x, const char *s, FILE *f)
4230 : {
4231 16 : fputc(x ? NAM_GEN : VAR_GEN,f);
4232 16 : wrstr(s, f);
4233 16 : if (x) wrGEN(x, f);
4234 16 : }
4235 :
4236 : /* read a GEN from file f */
4237 : static GEN
4238 24 : rdGEN(FILE *f)
4239 : {
4240 24 : size_t L = (size_t)rd_long(f);
4241 : GENbin *p;
4242 :
4243 24 : if (!L) return gen_0;
4244 24 : p = (GENbin*)pari_malloc(sizeof(GENbin) + L*sizeof(long));
4245 24 : p->len = L;
4246 24 : p->x = (GEN)rd_long(f);
4247 24 : p->base = (GEN)rd_long(f);
4248 24 : p->rebase = &shiftaddress_canon;
4249 24 : pari_fread_longs(GENbinbase(p), L,f);
4250 24 : return bin_copy(p);
4251 : }
4252 :
4253 : /* read a binary object in file f. Set *ptc to the object "type":
4254 : * BIN_GEN: an anonymous GEN x; return x.
4255 : * NAM_GEN: a named GEN x, with name v; set 'v to x (changevalue) and return x
4256 : * VAR_GEN: a name v; create the (unassigned) variable v and return gnil
4257 : * RELINK_TABLE: a relinking table for gen_relink(), to replace old adresses
4258 : * in * the original session by new incarnations in the current session.
4259 : * H is the current relinking table
4260 : * */
4261 : static GEN
4262 36 : readobj(FILE *f, int *ptc, hashtable *H)
4263 : {
4264 36 : int c = fgetc(f);
4265 36 : GEN x = NULL;
4266 36 : switch(c)
4267 : {
4268 8 : case BIN_GEN:
4269 8 : x = rdGEN(f);
4270 8 : if (H) gen_relink(x, H);
4271 8 : break;
4272 16 : case NAM_GEN:
4273 : case VAR_GEN:
4274 : {
4275 16 : char *s = rdstr(f);
4276 16 : if (!s) pari_err(e_MISC,"malformed binary file (no name)");
4277 16 : if (c == NAM_GEN)
4278 : {
4279 16 : x = rdGEN(f);
4280 16 : if (H) gen_relink(x, H);
4281 16 : err_printf("setting %s\n",s);
4282 16 : changevalue(varentries[fetch_user_var(s)], x);
4283 : }
4284 : else
4285 : {
4286 0 : pari_var_create(fetch_entry(s));
4287 0 : x = gnil;
4288 : }
4289 16 : break;
4290 : }
4291 0 : case RELINK_TABLE:
4292 0 : x = rdGEN(f); break;
4293 12 : case EOF: break;
4294 0 : default: pari_err(e_MISC,"unknown code in readobj");
4295 : }
4296 36 : *ptc = c; return x;
4297 : }
4298 :
4299 : #define MAGIC "\020\001\022\011-\007\020" /* ^P^A^R^I-^G^P */
4300 : #ifdef LONG_IS_64BIT
4301 : # define ENDIAN_CHECK 0x0102030405060708L
4302 : #else
4303 : # define ENDIAN_CHECK 0x01020304L
4304 : #endif
4305 : static const long BINARY_VERSION = 1; /* since 2.2.9 */
4306 :
4307 : static int
4308 79 : is_magic_ok(FILE *f)
4309 : {
4310 79 : pari_sp av = avma;
4311 79 : size_t L = strlen(MAGIC);
4312 79 : char *s = stack_malloc(L);
4313 79 : return gc_int(av, fread(s,1,L, f) == L && strncmp(s,MAGIC,L) == 0);
4314 : }
4315 :
4316 : static int
4317 12 : is_sizeoflong_ok(FILE *f)
4318 : {
4319 : char c;
4320 12 : return (fread(&c,1,1, f) == 1 && c == (char)sizeof(long));
4321 : }
4322 :
4323 : static int
4324 24 : is_long_ok(FILE *f, long L)
4325 : {
4326 : long c;
4327 24 : return (fread(&c,sizeof(long),1, f) == 1 && c == L);
4328 : }
4329 :
4330 : /* return 1 if valid binary file */
4331 : static int
4332 12 : check_magic(const char *name, FILE *f)
4333 : {
4334 12 : if (!is_magic_ok(f))
4335 0 : pari_warn(warner, "%s is not a GP binary file",name);
4336 12 : else if (!is_sizeoflong_ok(f))
4337 0 : pari_warn(warner, "%s not written for a %ld bit architecture",
4338 : name, sizeof(long)*8);
4339 12 : else if (!is_long_ok(f, ENDIAN_CHECK))
4340 0 : pari_warn(warner, "unexpected endianness in %s",name);
4341 12 : else if (!is_long_ok(f, BINARY_VERSION))
4342 0 : pari_warn(warner, "%s written by an incompatible version of GP",name);
4343 12 : else return 1;
4344 0 : return 0;
4345 : }
4346 :
4347 : static void
4348 12 : write_magic(FILE *f)
4349 : {
4350 12 : fprintf(f, MAGIC);
4351 12 : fprintf(f, "%c", (char)sizeof(long));
4352 12 : wr_long(ENDIAN_CHECK, f);
4353 12 : wr_long(BINARY_VERSION, f);
4354 12 : }
4355 :
4356 : int
4357 16 : file_is_binary(FILE *f)
4358 : {
4359 16 : int r, c = fgetc(f);
4360 16 : ungetc(c,f);
4361 16 : r = (c != EOF && isprint((unsigned char)c) == 0 && isspace((unsigned char)c) == 0);
4362 : #ifdef _WIN32
4363 : if (r) { setmode(fileno(f), _O_BINARY); rewind(f); }
4364 : #endif
4365 16 : return r;
4366 : }
4367 :
4368 : void
4369 12 : writebin(const char *name, GEN x)
4370 : {
4371 12 : FILE *f = fopen(name,"rb");
4372 12 : pari_sp av = avma;
4373 : GEN V;
4374 12 : int already = f? 1: 0;
4375 :
4376 12 : if (f) {
4377 0 : int ok = check_magic(name,f);
4378 0 : fclose(f);
4379 0 : if (!ok) pari_err_FILE("binary output file",name);
4380 : }
4381 12 : f = fopen(name,"ab");
4382 12 : if (!f) pari_err_FILE("binary output file",name);
4383 12 : if (!already) write_magic(f);
4384 :
4385 12 : V = copybin_unlink(x);
4386 12 : if (lg(gel(V,1)) > 1)
4387 : {
4388 0 : fputc(RELINK_TABLE,f);
4389 0 : wrGEN(V, f);
4390 : }
4391 12 : if (x) writeGEN(x,f);
4392 : else
4393 : {
4394 : entree *ep;
4395 : long i;
4396 544 : for (i = 0; i < functions_tblsz; i++)
4397 6160 : for (ep = functions_hash[i]; ep; ep = ep->next)
4398 5620 : if (EpVALENCE(ep) == EpVAR) writenamedGEN((GEN)ep->value,ep->name,f);
4399 : }
4400 12 : set_avma(av); fclose(f);
4401 12 : }
4402 :
4403 : /* read all objects in f. If f contains BIN_GEN that would be silently ignored
4404 : * [i.e f contains more than one objet, not all of them 'named GENs'], return
4405 : * them all in a vector and set 'vector'. */
4406 : GEN
4407 12 : readbin(const char *name, FILE *f, int *vector)
4408 : {
4409 12 : pari_sp av = avma;
4410 12 : hashtable *H = NULL;
4411 : pari_stack s_obj;
4412 : GEN obj, x, y;
4413 : int cy;
4414 12 : if (vector) *vector = 0;
4415 12 : if (!check_magic(name,f)) return NULL;
4416 12 : pari_stack_init(&s_obj, sizeof(GEN), (void**)&obj);
4417 : /* HACK: push codeword so as to be able to treat s_obj.data as a t_VEC */
4418 12 : pari_stack_pushp(&s_obj, (void*) (evaltyp(t_VEC)|_evallg(1)));
4419 12 : x = gnil;
4420 36 : while ((y = readobj(f, &cy, H)))
4421 : {
4422 24 : x = y;
4423 24 : switch(cy)
4424 : {
4425 8 : case BIN_GEN:
4426 8 : pari_stack_pushp(&s_obj, (void*)y); break;
4427 0 : case RELINK_TABLE:
4428 0 : if (H) hash_destroy(H);
4429 0 : H = hash_from_link(gel(y,1),gel(y,2), 0);
4430 : }
4431 : }
4432 12 : if (H) hash_destroy(H);
4433 12 : switch(s_obj.n) /* >= 1 */
4434 : {
4435 4 : case 1: break; /* nothing but the codeword */
4436 8 : case 2: x = gel(obj,1); break; /* read a single BIN_GEN */
4437 0 : default: /* more than one BIN_GEN */
4438 0 : setlg(obj, s_obj.n);
4439 0 : if (DEBUGLEVEL)
4440 0 : pari_warn(warner,"%ld unnamed objects read. Returning then in a vector",
4441 0 : s_obj.n - 1);
4442 0 : x = gc_GEN(av, obj);
4443 0 : if (vector) *vector = 1;
4444 : }
4445 12 : pari_stack_delete(&s_obj);
4446 12 : return x;
4447 : }
4448 :
4449 : /*******************************************************************/
4450 : /** **/
4451 : /** GP I/O **/
4452 : /** **/
4453 : /*******************************************************************/
4454 : /* print a vector of GENs, in output context 'out', using 'sep' as a
4455 : * separator between sucessive entries [ NULL = no separator ]*/
4456 :
4457 : static void
4458 148603 : str_print0(pari_str *S, const char *sep, GEN g, long flag)
4459 : {
4460 148603 : pari_sp av = avma;
4461 148603 : OUT_FUN f = get_fun(flag);
4462 148603 : long i, l = lg(g);
4463 444854 : for (i = 1; i < l; i++)
4464 : {
4465 296251 : GEN x = gel(g,i);
4466 296251 : if (typ(x) == t_STR) str_puts(S, GSTR(x)); else f(x, GP_DATA->fmt, S);
4467 296251 : if (sep && i+1 < l) str_puts(S, sep);
4468 296251 : if (!S->use_stack) set_avma(av);
4469 : }
4470 148603 : *(S->cur) = 0;
4471 148603 : }
4472 :
4473 : void
4474 116810 : out_print0(PariOUT *out, const char *sep, GEN g, long flag)
4475 : {
4476 116810 : pari_sp av = avma;
4477 : pari_str S;
4478 116810 : str_init(&S,1);
4479 116810 : str_print0(&S, sep, g, flag);
4480 116810 : str_putc(&S,'\n'); *(S.cur) = 0;
4481 116810 : out_puts(out, S.string);
4482 116810 : set_avma(av);
4483 116810 : }
4484 :
4485 : void
4486 20496 : out_print1(PariOUT *out, const char *sep, GEN g, long flag)
4487 : {
4488 20496 : pari_sp av = avma;
4489 : pari_str S;
4490 20496 : str_init(&S,1);
4491 20496 : str_print0(&S, sep, g, flag);
4492 20496 : out_puts(out, S.string);
4493 20496 : set_avma(av);
4494 20496 : }
4495 :
4496 : /* see print0(). Returns pari_malloc()ed string */
4497 : char *
4498 11189 : RgV_to_str(GEN g, long flag)
4499 : {
4500 11189 : pari_str S; str_init(&S,0);
4501 11189 : str_print0(&S, NULL, g, flag);
4502 11189 : return S.string;
4503 : }
4504 :
4505 : static GEN
4506 11189 : Str_fun(GEN g, long flag) {
4507 11189 : char *t = RgV_to_str(g, flag);
4508 11189 : GEN z = strtoGENstr(t);
4509 11189 : pari_free(t); return z;
4510 : }
4511 : GEN
4512 11063 : Str(GEN g) { return Str_fun(g, f_RAW); }
4513 : GEN
4514 126 : strtex(GEN g) { return Str_fun(g, f_TEX); }
4515 : GEN
4516 0 : strexpand(GEN g) {
4517 0 : char *s = RgV_to_str(g, f_RAW), *t = path_expand(s);
4518 0 : GEN z = strtoGENstr(t);
4519 0 : pari_free(t); pari_free(s); return z;
4520 : }
4521 :
4522 : /* display s, followed by the element of g */
4523 : char *
4524 14 : pari_sprint0(const char *s, GEN g, long flag)
4525 : {
4526 14 : pari_str S; str_init(&S, 0);
4527 14 : str_puts(&S, s);
4528 14 : str_print0(&S, NULL, g, flag);
4529 14 : return S.string;
4530 : }
4531 :
4532 : static void
4533 94 : print0_file(FILE *out, GEN g, long flag)
4534 : {
4535 94 : pari_sp av = avma;
4536 94 : pari_str S; str_init(&S, 1);
4537 94 : str_print0(&S, NULL, g, flag);
4538 94 : fputs(S.string, out);
4539 94 : set_avma(av);
4540 94 : }
4541 :
4542 : static void
4543 115487 : printfl_0(GEN g, long flag) { out_print0(pariOut, NULL, g, flag); }
4544 : static void
4545 20468 : printfl_1(GEN g, long flag) { out_print1(pariOut, NULL, g, flag); }
4546 : void
4547 1323 : printsep(const char *s, GEN g)
4548 1323 : { out_print0(pariOut, s, g, f_RAW); pari_flush(); }
4549 : void
4550 21 : printsep1(const char *s, GEN g)
4551 21 : { out_print1(pariOut, s, g, f_RAW); pari_flush(); }
4552 :
4553 : static char *
4554 80028 : sm_dopr(const char *fmt, GEN arg_vector, va_list args)
4555 : {
4556 80028 : pari_str s; str_init(&s, 0);
4557 80028 : str_arg_vprintf(&s, fmt, arg_vector, args);
4558 80007 : return s.string;
4559 : }
4560 : char *
4561 78565 : pari_vsprintf(const char *fmt, va_list ap)
4562 78565 : { return sm_dopr(fmt, NULL, ap); }
4563 :
4564 : /* dummy needed to pass an empty va_list to sm_dopr */
4565 : static char *
4566 1463 : dopr_arg_vector(GEN arg_vector, const char* fmt, ...)
4567 : {
4568 : va_list ap;
4569 : char *s;
4570 1463 : va_start(ap, fmt);
4571 1463 : s = sm_dopr(fmt, arg_vector, ap);
4572 1442 : va_end(ap); return s;
4573 : }
4574 : /* GP only */
4575 : void
4576 742 : printf0(const char *fmt, GEN args)
4577 742 : { char *s = dopr_arg_vector(args, fmt);
4578 721 : pari_puts(s); pari_free(s); pari_flush(); }
4579 : /* GP only */
4580 : GEN
4581 721 : strprintf(const char *fmt, GEN args)
4582 721 : { char *s = dopr_arg_vector(args, fmt);
4583 721 : GEN z = strtoGENstr(s); pari_free(s); return z; }
4584 :
4585 : void
4586 14698 : out_vprintf(PariOUT *out, const char *fmt, va_list ap)
4587 : {
4588 14698 : char *s = pari_vsprintf(fmt, ap);
4589 14698 : out_puts(out, s); pari_free(s);
4590 14698 : }
4591 : void
4592 753 : pari_vprintf(const char *fmt, va_list ap) { out_vprintf(pariOut, fmt, ap); }
4593 :
4594 : void
4595 355 : err_printf(const char* fmt, ...)
4596 : {
4597 355 : va_list args; va_start(args, fmt);
4598 355 : out_vprintf(pariErr,fmt,args); va_end(args);
4599 355 : }
4600 :
4601 : /* variadic version of printf0 */
4602 : void
4603 12669 : out_printf(PariOUT *out, const char *fmt, ...)
4604 : {
4605 12669 : va_list args; va_start(args,fmt);
4606 12669 : out_vprintf(out,fmt,args); va_end(args);
4607 12669 : }
4608 : void
4609 753 : pari_printf(const char *fmt, ...) /* variadic version of printf0 */
4610 : {
4611 753 : va_list args; va_start(args,fmt);
4612 753 : pari_vprintf(fmt,args); va_end(args);
4613 753 : }
4614 :
4615 : GEN
4616 2000 : gvsprintf(const char *fmt, va_list ap)
4617 : {
4618 2000 : char *s = pari_vsprintf(fmt, ap);
4619 2000 : GEN z = strtoGENstr(s);
4620 2000 : pari_free(s); return z;
4621 : }
4622 :
4623 : char *
4624 19146 : pari_sprintf(const char *fmt, ...) /* variadic version of strprintf */
4625 : {
4626 : char *s;
4627 : va_list ap;
4628 19146 : va_start(ap, fmt);
4629 19146 : s = pari_vsprintf(fmt, ap);
4630 19146 : va_end(ap); return s;
4631 : }
4632 :
4633 : void
4634 148096 : str_printf(pari_str *S, const char *fmt, ...)
4635 : {
4636 148096 : va_list ap; va_start(ap, fmt);
4637 148096 : str_arg_vprintf(S, fmt, NULL, ap);
4638 148096 : va_end(ap);
4639 148096 : }
4640 :
4641 : char *
4642 42721 : stack_sprintf(const char *fmt, ...)
4643 : {
4644 : char *s, *t;
4645 : va_list ap;
4646 42721 : va_start(ap, fmt);
4647 42721 : s = pari_vsprintf(fmt, ap);
4648 42721 : va_end(ap);
4649 42721 : t = stack_strdup(s);
4650 42721 : pari_free(s); return t;
4651 : }
4652 :
4653 : GEN
4654 1603 : gsprintf(const char *fmt, ...) /* variadic version of gvsprintf */
4655 : {
4656 : GEN s;
4657 : va_list ap;
4658 1603 : va_start(ap, fmt);
4659 1603 : s = gvsprintf(fmt, ap);
4660 1603 : va_end(ap); return s;
4661 : }
4662 :
4663 : /* variadic version of fprintf0. FIXME: fprintf0 not yet available */
4664 : void
4665 0 : pari_vfprintf(FILE *file, const char *fmt, va_list ap)
4666 : {
4667 0 : char *s = pari_vsprintf(fmt, ap);
4668 0 : fputs(s, file); pari_free(s);
4669 0 : }
4670 : void
4671 0 : pari_fprintf(FILE *file, const char *fmt, ...)
4672 : {
4673 0 : va_list ap; va_start(ap, fmt);
4674 0 : pari_vfprintf(file, fmt, ap); va_end(ap);
4675 0 : }
4676 :
4677 : void
4678 115438 : print (GEN g) { printfl_0(g, f_RAW); pari_flush(); }
4679 : void
4680 7 : printp (GEN g) { printfl_0(g, f_PRETTYMAT); pari_flush(); }
4681 : void
4682 42 : printtex(GEN g) { printfl_0(g, f_TEX); pari_flush(); }
4683 : void
4684 20468 : print1 (GEN g) { printfl_1(g, f_RAW); pari_flush(); }
4685 :
4686 : void
4687 14 : error0(GEN g)
4688 : {
4689 14 : if (lg(g)==2 && typ(gel(g,1))==t_ERROR) pari_err(0, gel(g,1));
4690 14 : else pari_err(e_USER, g);
4691 0 : }
4692 :
4693 : void
4694 7 : warning0(GEN g) { pari_warn(warnuser, g); }
4695 :
4696 : static void
4697 122 : wr_check(const char *t) {
4698 122 : if (GP_DATA->secure)
4699 : {
4700 0 : char *msg = pari_sprintf("[secure mode]: about to write to '%s'",t);
4701 0 : pari_ask_confirm(msg);
4702 0 : pari_free(msg);
4703 : }
4704 122 : }
4705 :
4706 : /* write to file s */
4707 : static void
4708 94 : wr(const char *s, GEN g, long flag, int addnl)
4709 : {
4710 94 : char *t = path_expand(s);
4711 : FILE *out;
4712 :
4713 94 : wr_check(t);
4714 94 : out = switchout_get_FILE(t);
4715 94 : print0_file(out, g, flag);
4716 94 : if (addnl) fputc('\n', out);
4717 94 : fflush(out);
4718 94 : if (fclose(out)) pari_warn(warnfile, "close", t);
4719 94 : pari_free(t);
4720 94 : }
4721 : void
4722 82 : write0 (const char *s, GEN g) { wr(s, g, f_RAW, 1); }
4723 : void
4724 4 : writetex(const char *s, GEN g) { wr(s, g, f_TEX, 1); }
4725 : void
4726 8 : write1 (const char *s, GEN g) { wr(s, g, f_RAW, 0); }
4727 : void
4728 12 : gpwritebin(const char *s, GEN x)
4729 : {
4730 12 : char *t = path_expand(s);
4731 12 : wr_check(t); writebin(t, x); pari_free(t);
4732 12 : }
4733 :
4734 : /*******************************************************************/
4735 : /** **/
4736 : /** HISTORY HANDLING **/
4737 : /** **/
4738 : /*******************************************************************/
4739 : /* history management function:
4740 : * p > 0, called from %p or %#p
4741 : * p <= 0, called from %` or %#` (|p| backquotes, possibly 0) */
4742 : static gp_hist_cell *
4743 61055 : history(long p)
4744 : {
4745 61055 : gp_hist *H = GP_DATA->hist;
4746 61055 : ulong t = H->total, s = H->size;
4747 : gp_hist_cell *c;
4748 :
4749 61055 : if (!t) pari_err(e_MISC,"The result history is empty");
4750 :
4751 61055 : if (p <= 0) p += t; /* count |p| entries starting from last */
4752 61055 : if (p <= 0 || p <= (long)(t - s) || (ulong)p > t)
4753 : {
4754 14 : long pmin = (long)(t - s) + 1;
4755 14 : if (pmin <= 0) pmin = 1;
4756 14 : pari_err(e_MISC,"History result %%%ld not available [%%%ld-%%%lu]",
4757 : p,pmin,t);
4758 : }
4759 61041 : c = H->v + ((p-1) % s);
4760 61041 : if (!c->z)
4761 7 : pari_err(e_MISC,"History result %%%ld has been deleted (histsize changed)", p);
4762 61034 : return c;
4763 : }
4764 : GEN
4765 61013 : pari_get_hist(long p) { return history(p)->z; }
4766 : long
4767 0 : pari_get_histtime(long p) { return history(p)->t; }
4768 : long
4769 0 : pari_get_histrtime(long p) { return history(p)->r; }
4770 : GEN
4771 21 : pari_histtime(long p) { return mkvec2s(history(p)->t, history(p)->r); }
4772 :
4773 : void
4774 108603 : pari_add_hist(GEN x, long time, long rtime)
4775 : {
4776 108603 : gp_hist *H = GP_DATA->hist;
4777 108603 : ulong i = H->total % H->size;
4778 108603 : H->total++;
4779 108603 : guncloneNULL(H->v[i].z);
4780 108603 : H->v[i].t = time;
4781 108603 : H->v[i].r = rtime;
4782 108603 : H->v[i].z = gclone(x);
4783 108603 : }
4784 :
4785 : ulong
4786 0 : pari_nb_hist(void)
4787 : {
4788 0 : return GP_DATA->hist->total;
4789 : }
4790 :
4791 : /*******************************************************************/
4792 : /** **/
4793 : /** TEMPORARY FILES **/
4794 : /** **/
4795 : /*******************************************************************/
4796 :
4797 : #ifndef R_OK
4798 : # define R_OK 4
4799 : # define W_OK 2
4800 : # define X_OK 1
4801 : # define F_OK 0
4802 : #endif
4803 :
4804 : #ifdef __EMX__
4805 : #include <io.h>
4806 : static int
4807 : unix_shell(void)
4808 : {
4809 : char *base, *sh = getenv("EMXSHELL");
4810 : if (!sh) {
4811 : sh = getenv("COMSPEC");
4812 : if (!sh) return 0;
4813 : }
4814 : base = _getname(sh);
4815 : return (stricmp (base, "cmd.exe") && stricmp (base, "4os2.exe")
4816 : && stricmp (base, "command.com") && stricmp (base, "4dos.com"));
4817 : }
4818 : #endif
4819 :
4820 : /* check if s has rwx permissions for us */
4821 : static int
4822 0 : pari_is_rwx(const char *s)
4823 : {
4824 : /* FIXME: HAS_ACCESS */
4825 : #if defined(UNIX) || defined (__EMX__)
4826 0 : return access(s, R_OK | W_OK | X_OK) == 0;
4827 : #else
4828 : (void) s; return 1;
4829 : #endif
4830 : }
4831 :
4832 : #if defined(UNIX) || defined (__EMX__)
4833 : #include <sys/types.h>
4834 : #include <sys/stat.h>
4835 : static int
4836 0 : pari_file_exists(const char *s)
4837 : {
4838 0 : int id = open(s, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
4839 0 : return id < 0 || close(id);
4840 : }
4841 : static int
4842 0 : pari_dir_exists(const char *s) { return mkdir(s, 0777); }
4843 : #elif defined(_WIN32)
4844 : static int
4845 : pari_file_exists(const char *s) { return GetFileAttributesA(s) != ~0UL; }
4846 : static int
4847 : pari_dir_exists(const char *s) { return mkdir(s); }
4848 : #else
4849 : static int
4850 : pari_file_exists(const char *s) { return 0; }
4851 : static int
4852 : pari_dir_exists(const char *s) { return 0; }
4853 : #endif
4854 :
4855 : static char *
4856 0 : env_ok(const char *s)
4857 : {
4858 0 : char *t = os_getenv(s);
4859 0 : if (t && !pari_is_rwx(t))
4860 : {
4861 0 : pari_warn(warner,"%s is set (%s), but is not writable", s,t);
4862 0 : t = NULL;
4863 : }
4864 0 : if (t && !pari_is_dir(t))
4865 : {
4866 0 : pari_warn(warner,"%s is set (%s), but is not a directory", s,t);
4867 0 : t = NULL;
4868 : }
4869 0 : return t;
4870 : }
4871 :
4872 : static const char*
4873 0 : pari_tmp_dir(void)
4874 : {
4875 : char *s;
4876 0 : s = env_ok("GPTMPDIR"); if (s) return s;
4877 0 : s = env_ok("TMPDIR"); if (s) return s;
4878 : #if defined(_WIN32) || defined(__EMX__)
4879 : s = env_ok("TMP"); if (s) return s;
4880 : s = env_ok("TEMP"); if (s) return s;
4881 : #endif
4882 : #if defined(UNIX) || defined(__EMX__)
4883 0 : if (pari_is_rwx("/tmp")) return "/tmp";
4884 0 : if (pari_is_rwx("/var/tmp")) return "/var/tmp";
4885 : #endif
4886 0 : return ".";
4887 : }
4888 :
4889 : /* loop through 26^2 variants [suffix 'aa' to 'zz'] */
4890 : static int
4891 0 : get_file(char *buf, int test(const char *), const char *suf)
4892 : {
4893 0 : char c, d, *end = buf + strlen(buf) - 1;
4894 0 : if (suf) end -= strlen(suf);
4895 0 : for (d = 'a'; d <= 'z'; d++)
4896 : {
4897 0 : end[-1] = d;
4898 0 : for (c = 'a'; c <= 'z'; c++)
4899 : {
4900 0 : *end = c;
4901 0 : if (! test(buf)) return 1;
4902 0 : if (DEBUGLEVEL) err_printf("I/O: file %s exists!\n", buf);
4903 : }
4904 : }
4905 0 : return 0;
4906 : }
4907 :
4908 : #if defined(__EMX__) || defined(_WIN32)
4909 : static void
4910 : swap_slash(char *s)
4911 : {
4912 : #ifdef __EMX__
4913 : if (!unix_shell())
4914 : #endif
4915 : {
4916 : char *t;
4917 : for (t=s; *t; t++)
4918 : if (*t == '/') *t = '\\';
4919 : }
4920 : }
4921 : #endif
4922 :
4923 : /* s truncated to 8 chars, suf possibly NULL */
4924 : static char *
4925 0 : init_unique(const char *s, const char *suf)
4926 : {
4927 0 : const char *pre = pari_tmp_dir();
4928 : char *buf, salt[64];
4929 : size_t lpre, lsalt, lsuf;
4930 : #ifdef UNIX
4931 0 : sprintf(salt,"-%ld-%ld", (long)getuid(), (long)getpid());
4932 : #else
4933 : sprintf(salt,"-%ld", (long)time(NULL));
4934 : #endif
4935 0 : lsuf = suf? strlen(suf): 0;
4936 0 : lsalt = strlen(salt);
4937 0 : lpre = strlen(pre);
4938 : /* room for prefix + '/' + s + salt + suf + '\0' */
4939 0 : buf = (char*) pari_malloc(lpre + 1 + 8 + lsalt + lsuf + 1);
4940 0 : strcpy(buf, pre);
4941 0 : if (buf[lpre-1] != '/') { (void)strcat(buf, "/"); lpre++; }
4942 : #if defined(__EMX__) || defined(_WIN32)
4943 : swap_slash(buf);
4944 : #endif
4945 0 : sprintf(buf + lpre, "%.8s%s", s, salt);
4946 0 : if (lsuf) strcat(buf, suf);
4947 0 : if (DEBUGLEVEL) err_printf("I/O: prefix for unique file/dir = %s\n", buf);
4948 0 : return buf;
4949 : }
4950 :
4951 : /* Return a "unique filename" built from the string s, possibly the user id
4952 : * and the process pid (on Unix systems). A "temporary" directory name is
4953 : * prepended. The name returned is pari_malloc'ed. It is DOS-safe
4954 : * (s truncated to 8 chars) */
4955 : char*
4956 0 : pari_unique_filename_suffix(const char *s, const char *suf)
4957 : {
4958 0 : char *buf = init_unique(s, suf);
4959 0 : if (pari_file_exists(buf) && !get_file(buf, pari_file_exists, suf))
4960 0 : pari_err(e_MISC,"couldn't find a suitable name for a tempfile (%s)",s);
4961 0 : return buf;
4962 : }
4963 : char*
4964 0 : pari_unique_filename(const char *s)
4965 0 : { return pari_unique_filename_suffix(s, NULL); }
4966 :
4967 : /* Create a "unique directory" and return its name built from the string
4968 : * s, the user id and process pid (on Unix systems). A "temporary"
4969 : * directory name is prepended. The name returned is pari_malloc'ed.
4970 : * It is DOS-safe (truncated to 8 chars) */
4971 : char*
4972 0 : pari_unique_dir(const char *s)
4973 : {
4974 0 : char *buf = init_unique(s, NULL);
4975 0 : if (pari_dir_exists(buf) && !get_file(buf, pari_dir_exists, NULL))
4976 0 : pari_err(e_MISC,"couldn't find a suitable name for a tempdir (%s)",s);
4977 0 : return buf;
4978 : }
4979 :
4980 : static long
4981 56 : get_free_gp_file(void)
4982 : {
4983 56 : long i, l = s_gp_file.n;
4984 56 : for (i=0; i<l; i++)
4985 0 : if (!gp_file[i].fp)
4986 0 : return i;
4987 56 : return pari_stack_new(&s_gp_file);
4988 : }
4989 :
4990 : static void
4991 320 : check_gp_file(const char *s, long n)
4992 : {
4993 320 : if (n < 0 || n >= s_gp_file.n || !gp_file[n].fp)
4994 20 : pari_err_FILEDESC(s, n);
4995 300 : }
4996 :
4997 : static long
4998 56 : new_gp_file(const char *s, FILE *f, int t)
4999 : {
5000 : long n;
5001 56 : n = get_free_gp_file();
5002 56 : gp_file[n].name = pari_strdup(s);
5003 56 : gp_file[n].fp = f;
5004 56 : gp_file[n].type = t;
5005 56 : gp_file[n].serial = gp_file_serial++;
5006 56 : if (DEBUGLEVEL) err_printf("fileopen:%ld (%ld)\n", n, gp_file[n].serial);
5007 56 : return n;
5008 : }
5009 :
5010 : #if defined(ZCAT) && defined(HAVE_PIPES)
5011 : static long
5012 36 : check_compress(const char *name)
5013 : {
5014 36 : long l = strlen(name);
5015 36 : const char *end = name + l-1;
5016 36 : if (l > 2 && (!strncmp(end-1,".Z",2)
5017 : #ifdef GNUZCAT
5018 36 : || !strncmp(end-2,".gz",3)
5019 : #endif
5020 : ))
5021 : { /* compressed file (compress or gzip) */
5022 0 : char *cmd = stack_malloc(strlen(ZCAT) + l + 4);
5023 0 : sprintf(cmd,"%s \"%s\"",ZCAT,name);
5024 0 : return gp_fileextern(cmd,"r");
5025 : }
5026 36 : return -1;
5027 : }
5028 : #endif
5029 :
5030 : long
5031 52 : gp_fileopen(const char *s, const char *mode)
5032 : {
5033 : FILE *f;
5034 52 : if (mode[0]==0 || mode[1]!=0)
5035 0 : pari_err_TYPE("fileopen",strtoGENstr(mode));
5036 52 : switch (mode[0])
5037 : {
5038 36 : case 'r':
5039 : #if defined(ZCAT) && defined(HAVE_PIPES)
5040 : {
5041 36 : long n = check_compress(s);
5042 36 : if (n >= 0) return n;
5043 : }
5044 : #endif
5045 36 : f = fopen(s, "r");
5046 36 : if (!f) pari_err_FILE("requested file", s);
5047 36 : return new_gp_file(s, f, mf_IN);
5048 16 : case 'w':
5049 : case 'a':
5050 16 : wr_check(s);
5051 16 : f = fopen(s, mode[0]=='w' ? "w": "a");
5052 16 : if (!f) pari_err_FILE("requested file", s);
5053 16 : return new_gp_file(s, f, mf_OUT);
5054 0 : default:
5055 0 : pari_err_TYPE("fileopen",strtoGENstr(mode));
5056 : return -1; /* LCOV_EXCL_LINE */
5057 : }
5058 : }
5059 :
5060 : long
5061 4 : gp_fileextern(const char *s, const char *mode)
5062 : {
5063 : FILE *f;
5064 4 : if (mode[0]==0 || mode[1]!=0)
5065 0 : pari_err_TYPE("fileopen",strtoGENstr(mode));
5066 4 : check_secure(s);
5067 4 : switch (mode[0])
5068 : {
5069 4 : case 'r':
5070 : #ifndef HAVE_PIPES
5071 : pari_err(e_ARCH,"pipes");
5072 : return NULL;/*LCOV_EXCL_LINE*/
5073 : #else
5074 4 : f = popen(s, "r");
5075 : #endif
5076 4 : if (!f) pari_err(e_MISC,"[pipe:] '%s' failed",s);
5077 4 : return new_gp_file(s, f, mf_PIPE|mf_IN);
5078 0 : case 'w':
5079 : #ifndef HAVE_PIPES
5080 : pari_err(e_ARCH,"pipes");
5081 : return NULL;/*LCOV_EXCL_LINE*/
5082 : #else
5083 0 : f = popen(s, "w");
5084 : #endif
5085 0 : if (!f) pari_err(e_MISC,"[pipe:] '%s' failed",s);
5086 0 : return new_gp_file(s, f, mf_PIPE|mf_OUT);
5087 0 : default:
5088 0 : pari_err_TYPE("fileextern",strtoGENstr(mode));
5089 : return -1; /* LCOV_EXCL_LINE */
5090 : }
5091 : }
5092 :
5093 : void
5094 56 : gp_fileclose(long n)
5095 : {
5096 56 : check_gp_file("fileclose", n);
5097 56 : if (DEBUGLEVEL) err_printf("fileclose(%ld)\n",n);
5098 56 : if (gp_file[n].type&mf_PIPE)
5099 4 : pclose(gp_file[n].fp);
5100 : else
5101 52 : fclose(gp_file[n].fp);
5102 56 : pari_free((void*)gp_file[n].name);
5103 56 : gp_file[n].name = NULL;
5104 56 : gp_file[n].fp = NULL;
5105 56 : gp_file[n].type = mf_FALSE;
5106 56 : gp_file[n].serial = -1;
5107 112 : while (s_gp_file.n > 0 && !gp_file[s_gp_file.n-1].fp)
5108 56 : s_gp_file.n--;
5109 56 : }
5110 :
5111 : void
5112 44 : gp_fileflush(long n)
5113 : {
5114 44 : check_gp_file("fileflush", n);
5115 40 : if (DEBUGLEVEL) err_printf("fileflush(%ld)\n",n);
5116 40 : if (gp_file[n].type == mf_OUT) (void)fflush(gp_file[n].fp);
5117 40 : }
5118 : void
5119 52 : gp_fileflush0(GEN gn)
5120 : {
5121 : long i;
5122 52 : if (gn)
5123 : {
5124 48 : if (typ(gn) != t_INT) pari_err_TYPE("fileflush",gn);
5125 44 : gp_fileflush(itos(gn));
5126 : }
5127 8 : else for (i = 0; i < s_gp_file.n; i++)
5128 4 : if (gp_file[i].fp && gp_file[i].type == mf_OUT) gp_fileflush(i);
5129 44 : }
5130 :
5131 : GEN
5132 64 : gp_fileread(long n)
5133 : {
5134 : Buffer *b;
5135 : FILE *fp;
5136 : GEN z;
5137 64 : check_gp_file("fileread", n);
5138 60 : if ((gp_file[n].type&mf_IN)==0)
5139 4 : pari_err_FILEDESC("fileread",n);
5140 56 : fp = gp_file[n].fp;
5141 56 : b = new_buffer();
5142 : while(1)
5143 : {
5144 56 : if (!gp_read_stream_buf(fp, b)) { delete_buffer(b); return gen_0; }
5145 48 : if (*(b->buf)) break;
5146 : }
5147 48 : z = strtoGENstr(b->buf);
5148 48 : delete_buffer(b);
5149 48 : return z;
5150 : }
5151 :
5152 : void
5153 48 : gp_filewrite(long n, const char *s)
5154 : {
5155 : FILE *fp;
5156 48 : check_gp_file("filewrite", n);
5157 44 : if ((gp_file[n].type&mf_OUT)==0)
5158 4 : pari_err_FILEDESC("filewrite",n);
5159 40 : fp = gp_file[n].fp;
5160 40 : fputs(s, fp);
5161 40 : fputc('\n',fp);
5162 40 : }
5163 :
5164 : void
5165 52 : gp_filewrite1(long n, const char *s)
5166 : {
5167 : FILE *fp;
5168 52 : check_gp_file("filewrite1", n);
5169 48 : if ((gp_file[n].type&mf_OUT)==0)
5170 4 : pari_err_FILEDESC("filewrite1",n);
5171 44 : fp = gp_file[n].fp;
5172 44 : fputs(s, fp);
5173 44 : }
5174 :
5175 : GEN
5176 56 : gp_filereadstr(long n)
5177 : {
5178 : Buffer *b;
5179 : char *s, *e;
5180 : GEN z;
5181 : int t;
5182 : input_method IM;
5183 56 : check_gp_file("filereadstr", n);
5184 52 : t = gp_file[n].type;
5185 52 : if ((t&mf_IN)==0)
5186 4 : pari_err_FILEDESC("fileread",n);
5187 48 : b = new_buffer();
5188 48 : IM.myfgets = (fgets_t)&fgets;
5189 48 : IM.file = (void*) gp_file[n].fp;
5190 48 : s = b->buf;
5191 48 : if (!file_getline(b, &s, &IM)) { delete_buffer(b); return gen_0; }
5192 44 : e = s + strlen(s)-1;
5193 44 : if (*e == '\n') *e = 0;
5194 44 : z = strtoGENstr(s);
5195 44 : delete_buffer(b);
5196 44 : return z;
5197 : }
5198 :
5199 : /*******************************************************************/
5200 : /** **/
5201 : /** INSTALL **/
5202 : /** **/
5203 : /*******************************************************************/
5204 :
5205 : #ifdef HAS_DLOPEN
5206 : #include <dlfcn.h>
5207 :
5208 : /* see try_name() */
5209 : static void *
5210 0 : try_dlopen(const char *s, int flag)
5211 0 : { void *h = dlopen(s, flag); pari_free((void*)s); return h; }
5212 :
5213 : /* like dlopen, but using default(sopath) */
5214 : static void *
5215 0 : gp_dlopen(const char *name, int flag)
5216 : {
5217 : void *handle;
5218 : char *s;
5219 :
5220 0 : if (!name) return dlopen(NULL, flag);
5221 0 : s = path_expand(name);
5222 :
5223 : /* if sopath empty or path is absolute, use dlopen */
5224 0 : if (!GP_DATA || *(GP_DATA->sopath->PATH)==0 || path_is_absolute(s))
5225 0 : return try_dlopen(s, flag);
5226 : else
5227 : {
5228 : forpath_t T;
5229 : char *t;
5230 0 : forpath_init(&T, GP_DATA->sopath, s);
5231 0 : while ( (t = forpath_next(&T)) )
5232 : {
5233 0 : if ( (handle = try_dlopen(t,flag)) ) { pari_free(s); return handle; }
5234 0 : (void)dlerror(); /* clear error message */
5235 : }
5236 0 : pari_free(s);
5237 : }
5238 0 : return NULL;
5239 : }
5240 :
5241 : static void *
5242 0 : install0(const char *name, const char *lib)
5243 : {
5244 : void *handle;
5245 :
5246 : #ifndef RTLD_GLOBAL /* OSF1 has dlopen but not RTLD_GLOBAL*/
5247 : # define RTLD_GLOBAL 0
5248 : #endif
5249 0 : handle = gp_dlopen(lib, RTLD_LAZY|RTLD_GLOBAL);
5250 :
5251 0 : if (!handle)
5252 : {
5253 0 : const char *s = dlerror(); if (s) err_printf("%s\n\n",s);
5254 0 : if (lib) pari_err(e_MISC,"couldn't open dynamic library '%s'",lib);
5255 0 : pari_err(e_MISC,"couldn't open dynamic symbol table of process");
5256 : }
5257 0 : return dlsym(handle, name);
5258 : }
5259 : #else
5260 : # ifdef _WIN32
5261 : static HMODULE
5262 : try_LoadLibrary(const char *s)
5263 : { void *h = LoadLibrary(s); pari_free((void*)s); return h; }
5264 :
5265 : /* like LoadLibrary, but using default(sopath) */
5266 : static HMODULE
5267 : gp_LoadLibrary(const char *name)
5268 : {
5269 : HMODULE handle;
5270 : char *s = path_expand(name);
5271 :
5272 : /* if sopath empty or path is absolute, use LoadLibrary */
5273 : if (!GP_DATA || *(GP_DATA->sopath->PATH)==0 || path_is_absolute(s))
5274 : return try_LoadLibrary(s);
5275 : else
5276 : {
5277 : forpath_t T;
5278 : char *t;
5279 : forpath_init(&T, GP_DATA->sopath, s);
5280 : while ( (t = forpath_next(&T)) )
5281 : if ( (handle = try_LoadLibrary(t)) ) { pari_free(s); return handle; }
5282 : pari_free(s);
5283 : }
5284 : return NULL;
5285 : }
5286 : static void *
5287 : install0(const char *name, const char *lib)
5288 : {
5289 : HMODULE handle;
5290 : if (lib == pari_library_path)
5291 : {
5292 : handle = GetModuleHandleA(NULL);
5293 : void * fun = (void *) GetProcAddress(handle,name);
5294 : if (fun) return fun;
5295 : }
5296 : handle = gp_LoadLibrary(lib);
5297 : if (!handle)
5298 : {
5299 : if (lib) pari_err(e_MISC,"couldn't open dynamic library '%s'",lib);
5300 : pari_err(e_MISC,"couldn't open dynamic symbol table of process");
5301 : }
5302 : return (void *) GetProcAddress(handle,name);
5303 : }
5304 : # else
5305 : static void *
5306 : install0(const char *name, const char *lib)
5307 : { pari_err(e_ARCH,"install"); return NULL; }
5308 : #endif
5309 : #endif
5310 :
5311 : static char *
5312 0 : dft_help(const char *gp, const char *s, const char *code)
5313 0 : { return stack_sprintf("%s: installed function\nlibrary name: %s\nprototype: %s", gp, s, code); }
5314 :
5315 : void
5316 0 : gpinstall(const char *s, const char *code, const char *gpname, const char *lib)
5317 : {
5318 0 : pari_sp av = avma;
5319 0 : const char *gp = *gpname? gpname: s;
5320 : int update_help;
5321 : void *f;
5322 : entree *ep;
5323 0 : if (GP_DATA->secure)
5324 : {
5325 0 : char *msg = pari_sprintf("[secure mode]: about to install '%s'", s);
5326 0 : pari_ask_confirm(msg);
5327 0 : pari_free(msg);
5328 : }
5329 0 : f = install0(s, *lib ?lib :pari_library_path);
5330 0 : if (!f)
5331 : {
5332 0 : if (*lib) pari_err(e_MISC,"can't find symbol '%s' in library '%s'",s,lib);
5333 0 : pari_err(e_MISC,"can't find symbol '%s' in dynamic symbol table of process",s);
5334 : }
5335 0 : ep = is_entry(gp);
5336 : /* Delete help if 1) help is the default (don't delete user addhelp)
5337 : * and 2) default help changes */
5338 0 : update_help = (ep && ep->valence == EpINSTALL && ep->help
5339 0 : && strcmp(ep->code, code)
5340 0 : && !strcmp(ep->help, dft_help(gp,s,ep->code)));
5341 0 : ep = install(f,gp,code);
5342 0 : if (update_help || !ep->help) addhelp(gp, dft_help(gp,s,code));
5343 0 : mt_broadcast(snm_closure(is_entry("install"),
5344 : mkvec4(strtoGENstr(s),strtoGENstr(code),
5345 : strtoGENstr(gp),strtoGENstr(lib))));
5346 0 : set_avma(av);
5347 0 : }
|