source: git/Singular/links/ssiLink.cc @ bdda8c2

fieker-DuValspielwiese
Last change on this file since bdda8c2 was bdda8c2, checked in by Hans Schoenemann <hannes@…>, 11 years ago
add: handling signals in system calls, p2
  • Property mode set to 100644
File size: 52.7 KB
Line 
1/****************************************
2 * Computer Algebra System SINGULAR     *
3 ****************************************/
4/***************************************************************
5 * File:    ssiLink.h
6 *  Purpose: declaration of sl_link routines for ssi
7 ***************************************************************/
8#include <stdio.h>
9#include <fcntl.h>
10#include <errno.h>
11#include <unistd.h>
12#include <sys/types.h>
13#include <signal.h>
14#include <sys/types.h>          /* for portability */
15#include <sys/select.h>
16#include <sys/socket.h>
17#include <netinet/in.h>
18#include <ctype.h>   /*for isdigit*/
19#include <netdb.h>
20#include <sys/wait.h>
21#include <time.h>
22
23
24
25#include "config.h"
26#include <kernel/mod2.h>
27#include <Singular/si_signals.h>
28
29#include <omalloc/omalloc.h>
30
31#include <misc/intvec.h>
32#include <misc/options.h>
33
34#include <polys/monomials/ring.h>
35#include <polys/matpol.h>
36
37#include <coeffs/bigintmat.h>
38
39#include <kernel/ideals.h>
40#include <kernel/polys.h>
41#include <kernel/longrat.h>
42#include <kernel/ideals.h>
43#include <kernel/timer.h>
44
45#include <Singular/tok.h>
46#include <Singular/ipshell.h>
47#include <Singular/ipid.h>
48
49#include <Singular/subexpr.h>
50#include <Singular/cntrlc.h>
51#include <Singular/lists.h>
52#include <Singular/blackbox.h>
53
54#include <Singular/links/silink.h>
55#include <Singular/links/s_buff.h>
56#include <Singular/links/ssiLink.h>
57
58// TODO: use number n_InitMPZ(mpz_t n, coeffs_BIGINT) instead!?
59
60struct snumber_dummy
61{
62  mpz_t z;
63  mpz_t n;
64  #if defined(LDEBUG)
65  int debug;
66  #endif
67  BOOLEAN s;
68};
69typedef struct snumber_dummy  *number_dummy;
70
71#ifdef HAVE_SIMPLEIPC
72#include <Singular/simpleipc.h>
73#endif
74//#if (_POSIX_C_SOURCE >= 200112L) || (_XOPEN_SOURCE >= 600)
75//#define HAVE_PSELECT
76//#endif
77
78#define SSI_VERSION 5
79
80// 64 bit version:
81//#if SIZEOF_LONG == 8
82#if 0
83#define MAX_NUM_SIZE 60
84#define POW_2_28 (1L<<60)
85#define LONG long
86#else
87// 32 bit version:
88#define MAX_NUM_SIZE 28
89#define POW_2_28 (1L<<28)
90#define LONG int
91#endif
92typedef struct
93{
94  s_buff f_read;
95  FILE *f_write;
96  ring r;
97  pid_t pid; /* only valid for fork/tcp mode*/
98  int fd_read,fd_write; /* only valid for fork/tcp mode*/
99  char level;
100  char send_quit_at_exit;
101  char quit_sent;
102
103} ssiInfo;
104
105link_list ssiToBeClosed=NULL;
106BOOLEAN ssiToBeClosed_inactive=TRUE;
107#define SSI_BLOCK_CHLD sigprocmask(SIG_SETMASK, &ssi_sigmask, &ssi_oldmask)
108#define SSI_UNBLOCK_CHLD sigprocmask(SIG_SETMASK, &ssi_oldmask, NULL)
109
110#define SR_INT 1L
111#define SR_HDL(A) ((long)(A))
112#define SR_TO_INT(SR)   (((long)SR) >> 2)
113
114// the helper functions:
115void ssiSetCurrRing(const ring r)
116{
117  if (!rEqual(r,currRing,1))
118  {
119    char name[20];
120    int nr=0;
121    do
122    { sprintf(name,"ssiRing%d",nr); nr++; }
123    while(IDROOT->get(name, 0)!=NULL);
124    idhdl h=enterid(omStrDup(name),0,RING_CMD,&IDROOT,FALSE);
125    IDRING(h)=r;
126    r->ref++;
127    rSetHdl(h);
128  }
129}
130// the implementation of the functions:
131void ssiWriteInt(ssiInfo *d,const int i)
132{
133  SSI_BLOCK_CHLD;
134  fprintf(d->f_write,"%d ",i);
135  SSI_UNBLOCK_CHLD;
136  //if (d->f_debug!=NULL) fprintf(d->f_debug,"int: %d ",i);
137}
138
139void ssiWriteString(ssiInfo *d,const char *s)
140{
141  SSI_BLOCK_CHLD;
142  fprintf(d->f_write,"%d %s ",(int)strlen(s),s);
143  SSI_UNBLOCK_CHLD;
144  //if (d->f_debug!=NULL) fprintf(d->f_debug,"stringi: %d \"%s\" ",strlen(s),s);
145}
146
147
148void ssiWriteBigInt(const ssiInfo *d, const number n)
149{
150  // syntax is as follows:
151  // case 2 Q:     3 4 <int>
152  //        or     3 3 <mpz_t nominator>
153  SSI_BLOCK_CHLD;
154  if(SR_HDL(n) & SR_INT)
155  {
156    fprintf(d->f_write,"4 %ld ",SR_TO_INT(n));
157    //if (d->f_debug!=NULL) fprintf(d->f_debug,"bigint: short \"%ld\" ",SR_TO_INT(n));
158  }
159  else if (((number_dummy)n)->s==3)
160  {
161    fputs("3 ",d->f_write);
162    mpz_out_str(d->f_write,10,((number_dummy)n)->z);
163    fputc(' ',d->f_write);
164    //gmp_fprintf(d->f_write,"3 %Zd ",n->z);
165    //if (d->f_debug!=NULL) gmp_fprintf(d->f_debug,"bigint: gmp \"%Zd\" ",n->z);
166  }
167  else WerrorS("illiegal bigint");
168  SSI_UNBLOCK_CHLD;
169}
170
171void ssiWriteNumber(const ssiInfo *d, const number n)
172{
173  // syntax is as follows:
174  // case 1 Z/p:   3 <int>
175  // case 2 Q:     3 4 <int>
176  //        or     3 0 <mpz_t nominator> <mpz_t denominator>
177  //        or     3 1  dto.
178  //        or     3 3 <mpz_t nominator>
179  //        or     3 5 <mpz_t raw nom.> <mpz_t raw denom.>
180  //        or     3 6 <mpz_t raw nom.> <mpz_t raw denom.>
181  //        or     3 7 <mpz_t raw nom.>
182  SSI_BLOCK_CHLD;
183  if(rField_is_Zp(d->r))
184  {
185    fprintf(d->f_write,"%d ",(int)(long)n);
186    //if (d->f_debug!=NULL) fprintf(d->f_debug,"number: \"%ld\" ",(int)(long)n);
187  }
188  else if (rField_is_Q(d->r))
189  {
190    if(SR_HDL(n) & SR_INT)
191    {
192      #if SIZEOF_LONG == 4
193      fprintf(d->f_write,"4 %d ",((LONG)SR_TO_INT(n)));
194      #else
195      long nn=SR_TO_INT(n);
196      if ((nn<POW_2_28)||(nn>= -POW_2_28))
197        fprintf(d->f_write,"4 %d ",((LONG)nn)); // NOTE: use %ld once LONG can be long !!!
198      else
199      {
200        mpz_t tmp;
201        mpz_init_set_si(tmp,nn);
202        fputs("8 ",d->f_write);
203        mpz_out_str (d->f_write,32, tmp);
204        fputc(' ',d->f_write);
205        mpz_clear(tmp);
206      }
207      #endif
208      //if (d->f_debug!=NULL) fprintf(d->f_debug,"number: short \"%ld\" ",SR_TO_INT(n));
209    }
210    else if (((number_dummy)n)->s<2)
211    {
212      //gmp_fprintf(d->f_write,"%d %Zd %Zd ",n->s,n->z,n->n);
213      fprintf(d->f_write,"%d ",((number_dummy)n)->s+5);
214      mpz_out_str (d->f_write,32, ((number_dummy)n)->z);
215      fputc(' ',d->f_write);
216      mpz_out_str (d->f_write,32, ((number_dummy)n)->n);
217      fputc(' ',d->f_write);
218
219      //if (d->f_debug!=NULL) gmp_fprintf(d->f_debug,"number: s=%d gmp/gmp \"%Zd %Zd\" ",n->s,n->z,n->n);
220    }
221    else /*n->s==3*/
222    {
223      //gmp_fprintf(d->f_write,"3 %Zd ",n->z);
224      fputs("8 ",d->f_write);
225      mpz_out_str (d->f_write,32, ((number_dummy)n)->z);
226      fputc(' ',d->f_write);
227
228      //if (d->f_debug!=NULL) gmp_fprintf(d->f_debug,"number: gmp \"%Zd\" ",n->z);
229    }
230  }
231  else WerrorS("coeff field not implemented");
232  SSI_UNBLOCK_CHLD;
233}
234
235void ssiWriteRing(ssiInfo *d,const ring r)
236{
237  /* 5 <ch> <N> <l1> <v1> ...<lN> <vN> <number of orderings> <ord1> <block0_1> <block1_1> .... */
238  if (d->r!=NULL) rKill(d->r);
239  d->r=r;
240  d->r->ref++;
241  SSI_BLOCK_CHLD;
242  fprintf(d->f_write,"%d %d ",n_GetChar(r->cf),r->N);
243
244  int i;
245  for(i=0;i<r->N;i++)
246  {
247    fprintf(d->f_write,"%d %s ",(int)strlen(r->names[i]),r->names[i]);
248  }
249  /* number of orderings:*/
250  i=0;
251  while (r->order[i]!=0) i++;
252  fprintf(d->f_write,"%d ",i);
253  /* each ordering block: */
254  i=0;
255  while(r->order[i]!=0)
256  {
257    fprintf(d->f_write,"%d %d %d ",r->order[i],r->block0[i], r->block1[i]);
258    switch(r->order[i])
259    {
260      case ringorder_a:
261      case ringorder_wp:
262      case ringorder_Wp:
263      case ringorder_ws:
264      case ringorder_Ws:
265      case ringorder_aa:
266      {
267        int ii;
268        for(ii=r->block0[i];ii<=r->block1[i];ii++)
269          fprintf(d->f_write,"%d ",r->wvhdl[i][ii-r->block0[i]]);
270      }
271      break;
272
273      case ringorder_a64:
274      case ringorder_M:
275      case ringorder_L:
276      case ringorder_IS:
277        Werror("ring oder not implemented for ssi:%d",r->order[i]);
278        break;
279
280      default: break;
281    }
282    i++;
283  }
284  SSI_UNBLOCK_CHLD;
285}
286
287void ssiWritePoly(ssiInfo *d, int /*typ*/, poly p)
288{
289  SSI_BLOCK_CHLD;
290  fprintf(d->f_write,"%d ",pLength(p));//number of terms
291  SSI_UNBLOCK_CHLD;
292  int j;
293
294  while(p!=NULL)
295  {
296    ssiWriteNumber(d,pGetCoeff(p));
297    //nWrite(fich,pGetCoeff(p));
298    SSI_BLOCK_CHLD;
299    fprintf(d->f_write,"%ld ",p_GetComp(p,d->r));//component
300
301    for(j=1;j<=rVar(d->r);j++)
302    {
303      fprintf(d->f_write,"%ld ",p_GetExp(p,j,d->r ));//x^j
304    }
305    pIter(p);
306    SSI_UNBLOCK_CHLD;
307  }
308}
309
310void ssiWriteIdeal(ssiInfo *d, int typ,ideal I)
311{
312   // syntax: 7 # of elements <poly 1> <poly2>.....
313   // syntax: 8 <rows> <cols> <poly 1> <poly2>.....
314   matrix M=(matrix)I;
315   int mn;
316   SSI_BLOCK_CHLD;
317   if (typ==MATRIX_CMD)
318   {
319     mn=MATROWS(M)*MATCOLS(M);
320     fprintf(d->f_write,"%d %d ", MATROWS(M),MATCOLS(M));
321   }
322   else
323   {
324     mn=IDELEMS(I);
325     fprintf(d->f_write,"%d ",IDELEMS(I));
326   }
327    SSI_UNBLOCK_CHLD;
328
329   int i;
330   int tt;
331   if (typ==MODUL_CMD) tt=VECTOR_CMD;
332   else                tt=POLY_CMD;
333
334   for(i=0;i<mn;i++)
335   {
336     ssiWritePoly(d,tt,I->m[i]);
337   }
338}
339
340void ssiWriteCommand(si_link l, command D)
341{
342  ssiInfo *d=(ssiInfo*)l->data;
343  // syntax: <num ops> <operation> <op1> <op2> ....
344  SSI_BLOCK_CHLD;
345  fprintf(d->f_write,"%d %d ",D->argc,D->op);
346  SSI_UNBLOCK_CHLD;
347  if (D->argc >0) ssiWrite(l, &(D->arg1));
348  if (D->argc < 4)
349  {
350    if (D->argc >1) ssiWrite(l, &(D->arg2));
351    if (D->argc >2) ssiWrite(l, &(D->arg3));
352  }
353}
354
355void ssiWriteProc(ssiInfo *d,procinfov p)
356{
357  if (p->data.s.body==NULL)
358    iiGetLibProcBuffer(p);
359  if (p->data.s.body!=NULL)
360    ssiWriteString(d,p->data.s.body);
361  else
362    ssiWriteString(d,"");
363}
364
365void ssiWriteList(si_link l,lists dd)
366{
367  ssiInfo *d=(ssiInfo*)l->data;
368  int Ll=lSize(dd);
369  SSI_BLOCK_CHLD;
370  fprintf(d->f_write,"%d ",Ll+1);
371  SSI_UNBLOCK_CHLD;
372  int i;
373  for(i=0;i<=Ll;i++)
374  {
375    ssiWrite(l,&(dd->m[i]));
376  }
377}
378void ssiWriteIntvec(ssiInfo *d,intvec * v)
379{
380  SSI_BLOCK_CHLD;
381  fprintf(d->f_write,"%d ",v->length());
382  int i;
383  for(i=0;i<v->length();i++)
384  {
385    fprintf(d->f_write,"%d ",(*v)[i]);
386  }
387  SSI_UNBLOCK_CHLD;
388}
389void ssiWriteIntmat(ssiInfo *d,intvec * v)
390{
391  SSI_BLOCK_CHLD;
392  fprintf(d->f_write,"%d %d ",v->rows(),v->cols());
393  int i;
394  for(i=0;i<v->length();i++)
395  {
396    fprintf(d->f_write,"%d ",(*v)[i]);
397  }
398  SSI_UNBLOCK_CHLD;
399}
400
401void ssiWriteBigintmat(ssiInfo *d,bigintmat * v)
402{
403  SSI_BLOCK_CHLD;
404  fprintf(d->f_write,"%d %d ",v->rows(),v->cols());
405  int i;
406  for(i=0;i<v->length();i++)
407  {
408    ssiWriteBigInt(d,(*v)[i]);
409  }
410  SSI_UNBLOCK_CHLD;
411}
412
413char *ssiReadString(ssiInfo *d)
414{
415  char *buf;
416  int l;
417  l=s_readint(d->f_read);
418  buf=(char*)omAlloc0(l+1);
419  /*int c =*/ (void) s_getc(d->f_read); /* skip ' '*/
420  /*int ll=*/ (void) s_readbytes(buf,l,d->f_read);
421  //if (ll!=l) printf("want %d, got %d bytes\n",l,ll);
422  buf[l]='\0';
423  return buf;
424}
425
426int ssiReadInt(s_buff fich)
427{
428  return s_readint(fich);
429}
430
431number ssiReadBigInt(ssiInfo *d)
432{
433  int sub_type=-1;
434  sub_type=s_readint(d->f_read);
435  switch(sub_type)
436  {
437   case 3:
438     {// read int or mpz_t or mpz_t, mpz_t
439       number n=(number)omAlloc0(sizeof(snumber_dummy));
440       s_readmpz(d->f_read,((number_dummy)n)->z);
441       ((number_dummy)n)->s=sub_type;
442       return n;
443     }
444   case 4:
445     {
446       int dd;
447       dd=s_readint(d->f_read);
448       return n_Init(dd,d->r->cf);
449     }
450   default:
451       Werror("error in reading bigint: invalid subtype %d",sub_type);
452       return NULL;
453   }
454}
455
456static number ssiReadQNumber(ssiInfo *d)
457{
458  int sub_type=-1;
459  sub_type=s_readint(d->f_read);
460  switch(sub_type)
461  {
462     case 0:
463     case 1:
464       {// read mpz_t, mpz_t
465         number n=(number)omAlloc0(sizeof(snumber_dummy));
466         mpz_init(((number_dummy)n)->z);
467         mpz_init(((number_dummy)n)->n);
468         s_readmpz(d->f_read,((number_dummy)n)->z);
469         s_readmpz(d->f_read,((number_dummy)n)->n);
470         ((number_dummy)n)->s=sub_type;
471         return n;
472       }
473
474     case 3:
475       {// read mpz_t
476         number n=(number)omAlloc0(sizeof(snumber_dummy));
477         mpz_init(((number_dummy)n)->z);
478         s_readmpz(d->f_read,((number_dummy)n)->z);
479         ((number_dummy)n)->s=3; /*sub_type*/
480         return n;
481       }
482     case 4:
483       {
484         int dd=s_readint(d->f_read);
485         return n_Init(dd,coeffs_BIGINT);
486       }
487     case 5:
488     case 6:
489       {// read raw mpz_t, mpz_t
490         number n=(number)omAlloc0(sizeof(snumber_dummy));
491         mpz_init(((number_dummy)n)->z);
492         mpz_init(((number_dummy)n)->n);
493         s_readmpz_base (d->f_read,((number_dummy)n)->z, 32);
494         s_readmpz_base (d->f_read,((number_dummy)n)->n, 32);
495         ((number_dummy)n)->s=sub_type-5;
496         return n;
497       }
498     case 8:
499       {// read raw mpz_t
500         number n=(number)omAlloc0(sizeof(snumber_dummy));
501         mpz_init(((number_dummy)n)->z);
502         s_readmpz_base (d->f_read,((number_dummy)n)->z, 32);
503         ((number_dummy)n)->s=sub_type=3; /*subtype-5*/
504         return n;
505       }
506
507     default: Werror("error in reading number: invalid subtype %d",sub_type);
508              return NULL;
509  }
510  return NULL;
511}
512number ssiReadNumber(ssiInfo *d)
513{
514  if (rField_is_Q(d->r))
515  {
516     return ssiReadQNumber(d);
517  }
518  else if (rField_is_Zp(d->r))
519  {
520    // read int
521    int dd=s_readint(d->f_read);
522    return (number)dd;
523  }
524  else Werror("coeffs not implemented");
525  return NULL;
526}
527
528ring ssiReadRing(ssiInfo *d)
529{
530/* syntax is <ch> <N> <l1> <v1> ...<lN> <vN> <number of orderings> <ord1> <block0_1> <block1_1> .... */
531  int ch, N,i/*,l*/;
532  char **names;
533  ch=s_readint(d->f_read);
534  N=s_readint(d->f_read);
535  names=(char**)omAlloc(N*sizeof(char*));
536  for(i=0;i<N;i++)
537  {
538    names[i]=ssiReadString(d);
539  }
540  // read the orderings:
541  int num_ord; // number of orderings
542  num_ord=s_readint(d->f_read);
543  int *ord=(int *)omAlloc0((num_ord+1)*sizeof(int));
544  int *block0=(int *)omAlloc0((num_ord+1)*sizeof(int));
545  int *block1=(int *)omAlloc0((num_ord+1)*sizeof(int));
546  int **wvhdl=(int**)omAlloc0((num_ord+1)*sizeof(int*));
547  for(i=0;i<num_ord;i++)
548  {
549    ord[i]=s_readint(d->f_read);
550    block0[i]=s_readint(d->f_read);
551    block1[i]=s_readint(d->f_read);
552    switch(ord[i])
553    {
554      case ringorder_a:
555      case ringorder_wp:
556      case ringorder_Wp:
557      case ringorder_ws:
558      case ringorder_Ws:
559      case ringorder_aa:
560      {
561        wvhdl[i]=(int*)omAlloc((block1[i]-block0[i]+1)*sizeof(int));
562        int ii;
563        for(ii=block0[i];ii<=block1[i];ii++)
564          wvhdl[i][ii-block0[i]]=s_readint(d->f_read);
565      }
566      break;
567
568      case ringorder_a64:
569      case ringorder_M:
570      case ringorder_L:
571      case ringorder_IS:
572        Werror("ring oder not implemented for ssi:%d",ord[i]);
573        break;
574
575      default: break;
576    }
577  }
578  return rDefault(ch,N,names,num_ord,ord,block0,block1,wvhdl);
579}
580
581poly ssiReadPoly(ssiInfo *D)
582{
583// < # of terms> < term1> < .....
584  int n,i,l;
585  n=ssiReadInt(D->f_read);
586  //Print("poly: terms:%d\n",n);
587  poly p;
588  // int j;
589  // j=0;
590  poly ret=NULL;
591  poly prev=NULL;
592  for(l=0;l<n;l++) // read n terms
593  {
594// coef,comp.exp1,..exp N
595    p=p_Init(D->r);
596    pGetCoeff(p)=ssiReadNumber(D);
597    int d=s_readint(D->f_read);
598    p_SetComp(p,d,D->r);
599    for(i=1;i<=rVar(D->r);i++)
600    {
601      d=s_readint(D->f_read);
602      p_SetExp(p,i,d,D->r);
603    }
604    p_Setm(p,D->r);
605    p_Test(p,D->r);
606    if (ret==NULL) ret=p;
607    else           pNext(prev)=p;
608    prev=p;
609 }
610 return ret;
611}
612
613ideal ssiReadIdeal(ssiInfo *d)
614{
615  int i;
616  int n=s_readint(d->f_read);
617  ideal I=idInit(n,1);
618  for(i=0;i<IDELEMS(I);i++) // read n terms
619  {
620    I->m [i]=ssiReadPoly(d);
621  }
622  return I;
623}
624
625matrix ssiReadMatrix(ssiInfo *d)
626{
627  int m=s_readint(d->f_read);
628  int n=s_readint(d->f_read);
629  matrix M=mpNew(m,n);
630  poly p;
631  for(int i=1;i<=MATROWS(M);i++)
632    for(int j=1;j<=MATCOLS(M);j++)
633    {
634      p=ssiReadPoly(d);
635      MATELEM(M,i,j)=p;
636    }
637  return M;
638}
639
640command ssiReadCommand(si_link l)
641{
642  ssiInfo *d=(ssiInfo*)l->data;
643  // syntax: <num ops> <operation> <op1> <op2> ....
644  command D=(command)omAlloc0(sizeof(*D));
645  int argc,op;
646  argc=s_readint(d->f_read);
647  op=s_readint(d->f_read);
648  D->argc=argc; D->op=op;
649  leftv v;
650  if (argc >0)
651  {
652    v=ssiRead1(l);
653    memcpy(&(D->arg1),v,sizeof(*v));
654    omFreeBin(v,sleftv_bin);
655  }
656  if (argc <4)
657  {
658    if (D->argc >1)
659    {
660      v=ssiRead1(l);
661      memcpy(&(D->arg2),v,sizeof(*v));
662      omFreeBin(v,sleftv_bin);
663    }
664    if (D->argc >2)
665    {
666      v=ssiRead1(l);
667      memcpy(&(D->arg3),v,sizeof(*v));
668      omFreeBin(v,sleftv_bin);
669    }
670  }
671  else
672  {
673    leftv prev=&(D->arg1);
674    argc--;
675    while(argc >0)
676    {
677      v=ssiRead1(l);
678      prev->next=v;
679      prev=v;
680      argc--;
681    }
682  }
683  return D;
684}
685
686procinfov ssiReadProc(ssiInfo *d)
687{
688  char *s=ssiReadString(d);
689  procinfov p=(procinfov)omAlloc0Bin(procinfo_bin);
690  p->language=LANG_SINGULAR;
691  p->libname=omStrDup("");
692  p->procname=omStrDup("");
693  p->data.s.body=s;
694  return p;
695}
696lists ssiReadList(si_link l)
697{
698  ssiInfo *d=(ssiInfo*)l->data;
699  int nr=s_readint(d->f_read);
700  lists L=(lists)omAlloc(sizeof(*L));
701  L->Init(nr);
702
703  int i;
704  leftv v;
705  for(i=0;i<nr;i++)
706  {
707    v=ssiRead1(l);
708    memcpy(&(L->m[i]),v,sizeof(*v));
709    omFreeBin(v,sleftv_bin);
710  }
711  return L;
712}
713intvec* ssiReadIntvec(ssiInfo *d)
714{
715  int nr=s_readint(d->f_read);
716  intvec *v=new intvec(nr);
717  for(int i=0;i<nr;i++)
718  {
719    (*v)[i]=s_readint(d->f_read);
720  }
721  return v;
722}
723intvec* ssiReadIntmat(ssiInfo *d)
724{
725  int r,c;
726  r=s_readint(d->f_read);
727  c=s_readint(d->f_read);
728  intvec *v=new intvec(r,c,0);
729  for(int i=0;i<r*c;i++)
730  {
731    (*v)[i]=s_readint(d->f_read);
732  }
733  return v;
734}
735bigintmat* ssiReadBigintmat(ssiInfo *d)
736{
737  int r,c;
738  r=s_readint(d->f_read);
739  c=s_readint(d->f_read);
740  bigintmat *v=new bigintmat(r,c,coeffs_BIGINT);
741  for(int i=0;i<r*c;i++)
742  {
743    (*v)[i]=ssiReadBigInt(d);
744  }
745  return v;
746}
747
748void ssiReadBlackbox(leftv res, si_link l)
749{
750  ssiInfo *d=(ssiInfo*)l->data;
751  /*int throwaway=*/ (void) s_readint(d->f_read);
752  char *name=ssiReadString(d);
753  int tok;
754  blackboxIsCmd(name,tok);
755  if (tok>MAX_TOK)
756  {
757    blackbox *b=getBlackboxStuff(tok);
758    res->rtyp=tok;
759    b->blackbox_deserialize(&b,&(res->data),l);
760  }
761  else
762  {
763    Werror("blackbox %s not found",name);
764  }
765}
766
767//**************************************************************************/
768
769BOOLEAN ssiOpen(si_link l, short flag, leftv u)
770{
771  if (l!=NULL)
772  {
773    const char *mode;
774    ssiInfo *d=(ssiInfo*)omAlloc0(sizeof(ssiInfo));
775    sigprocmask(SIG_SETMASK, NULL, &ssi_sigmask);
776    sigaddset(&ssi_sigmask, SIGCHLD);
777    if (flag & SI_LINK_OPEN)
778    {
779      if (l->mode[0] != '\0' && (strcmp(l->mode, "r") == 0))
780        flag = SI_LINK_READ;
781      else flag = SI_LINK_WRITE;
782    }
783
784    if (flag == SI_LINK_READ) mode = "r";
785    else if (strcmp(l->mode, "w") == 0) mode = "w";
786    else if (strcmp(l->mode, "fork") == 0) mode = "fork";
787    else if (strcmp(l->mode, "tcp") == 0) mode = "tcp";
788    else if (strcmp(l->mode, "connect") == 0) mode = "connect";
789    else mode = "a";
790
791
792    SI_LINK_SET_OPEN_P(l, flag);
793    l->data=d;
794    omFree(l->mode);
795    l->mode = omStrDup(mode);
796
797    if (l->name[0] == '\0')
798    {
799      if (strcmp(mode,"fork")==0)
800      {
801        link_list n=(link_list)omAlloc(sizeof(link_struct));
802        n->u=u;
803        n->l=l;
804        n->next=(void *)ssiToBeClosed;
805        ssiToBeClosed=n;
806
807        int pc[2];
808        int cp[2];
809        SSI_BLOCK_CHLD;
810        pipe(pc);
811        pipe(cp);
812        pid_t pid=fork();
813        SSI_UNBLOCK_CHLD;
814        if (pid==0) /*fork: child*/
815        {
816          link_list hh=(link_list)ssiToBeClosed->next;
817          /* we know: l is the first entry in ssiToBeClosed-list */
818          while(hh!=NULL)
819          {
820            SI_LINK_SET_CLOSE_P(hh->l);
821            ssiInfo *dd=(ssiInfo*)hh->l->data;
822            SSI_BLOCK_CHLD;
823            s_close(dd->f_read);
824            fclose(dd->f_write);
825            SSI_UNBLOCK_CHLD;
826            if (dd->r!=NULL) rKill(dd->r);
827            omFreeSize((ADDRESS)dd,(sizeof *dd));
828            hh->l->data=NULL;
829            link_list nn=(link_list)hh->next;
830            omFree(hh);
831            hh=nn;
832          }
833          ssiToBeClosed->next=NULL;
834          SSI_BLOCK_CHLD;
835          si_close(pc[1]); si_close(cp[0]);
836          d->f_write=fdopen(cp[1],"w");
837          SSI_UNBLOCK_CHLD;
838          d->f_read=s_open(pc[0]);
839          d->fd_read=pc[0];
840          d->fd_write=cp[1];
841          l->data=d;
842          omFree(l->mode);
843          l->mode = omStrDup(mode);
844          singular_in_batchmode=TRUE;
845          SI_LINK_SET_RW_OPEN_P(l);
846          //myynest=0;
847          fe_fgets_stdin=fe_fgets_dummy;
848          if ((u!=NULL)&&(u->rtyp==IDHDL))
849          {
850            idhdl h=(idhdl)u->data;
851            h->lev=0;
852          }
853          loop
854          {
855            leftv h=ssiRead1(l); /*contains an exit.... */
856            if (feErrors != NULL && *feErrors != '\0')
857            {
858              // handle errors:
859              PrintS(feErrors); /* currently quite simple */
860              *feErrors = '\0';
861            }
862            ssiWrite(l,h);
863            h->CleanUp();
864            omFreeBin(h, sleftv_bin);
865          }
866          /* never reached*/
867        }
868        else if (pid>0) /*fork: parent*/
869        {
870          d->pid=pid;
871          SSI_BLOCK_CHLD;
872          si_close(pc[0]); si_close(cp[1]);
873          d->f_write=fdopen(pc[1],"w");
874          SSI_UNBLOCK_CHLD;
875          d->f_read=s_open(cp[0]);
876          d->fd_read=cp[0];
877          d->fd_write=pc[1];
878          SI_LINK_SET_RW_OPEN_P(l);
879          d->send_quit_at_exit=1;
880        }
881        else
882        {
883          Werror("fork failed (%d)",errno);
884          l->data=NULL;
885          omFree(d);
886          return TRUE;
887        }
888      }
889      // ---------------------------------------------------------------------
890      else if (strcmp(mode,"tcp")==0)
891      {
892        int sockfd, newsockfd, portno, clilen;
893        struct sockaddr_in serv_addr, cli_addr;
894        // int n;
895        sockfd = socket(AF_INET, SOCK_STREAM, 0);
896        if(sockfd < 0)
897        {
898          WerrorS("ERROR opening socket");
899          l->data=NULL;
900          omFree(d);
901          return TRUE;
902        }
903        memset((char *) &serv_addr,0, sizeof(serv_addr));
904        portno = 1025;
905        serv_addr.sin_family = AF_INET;
906        serv_addr.sin_addr.s_addr = INADDR_ANY;
907        do
908        {
909          portno++;
910          serv_addr.sin_port = htons(portno);
911          if(portno > 50000)
912          {
913            WerrorS("ERROR on binding (no free port available?)");
914            l->data=NULL;
915            omFree(d);
916            return TRUE;
917          }
918        }
919        while(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0);
920        Print("waiting on port %d\n", portno);mflush();
921        listen(sockfd,1);
922        newsockfd = si_accept(sockfd, (struct sockaddr *) &cli_addr, (socklen_t *)&clilen);
923        if(newsockfd < 0)
924        {
925          WerrorS("ERROR on accept");
926          l->data=NULL;
927          omFree(d);
928          return TRUE;
929        }
930        PrintS("client accepted\n");
931        d->fd_read = newsockfd;
932        d->fd_write = newsockfd;
933        d->f_read = s_open(newsockfd);
934        d->f_write = fdopen(newsockfd, "w");
935        SI_LINK_SET_RW_OPEN_P(l);
936        si_close(sockfd);
937      }
938      // no ssi-Link on stdin or stdout
939      else
940      {
941        Werror("invalid mode >>%s<< for ssi",mode);
942        l->data=NULL;
943        omFree(d);
944        return TRUE;
945      }
946    }
947    // =========================================================================
948    else /*l->name=NULL*/
949    {
950      // tcp mode
951      if(strcmp(mode,"tcp")==0)
952      {
953        int sockfd, newsockfd, portno, clilen;
954        struct sockaddr_in serv_addr, cli_addr;
955        // int n;
956        sockfd = socket(AF_INET, SOCK_STREAM, 0);
957        if(sockfd < 0)
958        {
959          WerrorS("ERROR opening socket");
960          l->data=NULL;
961          omFree(d);
962          return TRUE;
963        }
964        memset((char *) &serv_addr,0, sizeof(serv_addr));
965        portno = 1025;
966        serv_addr.sin_family = AF_INET;
967        serv_addr.sin_addr.s_addr = INADDR_ANY;
968        do
969        {
970          portno++;
971          serv_addr.sin_port = htons(portno);
972          if(portno > 50000)
973          {
974            WerrorS("ERROR on binding (no free port available?)");
975            l->data=NULL;
976            return TRUE;
977          }
978        }
979        while(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0);
980        //Print("waiting on port %d\n", portno);mflush();
981        listen(sockfd,1);
982        char* cli_host = (char*)omAlloc(256);
983        char* path = (char*)omAlloc(1024);
984        int r = si_sscanf(l->name,"%255[^:]:%s",cli_host,path);
985        if(r == 0)
986        {
987          WerrorS("ERROR: no host specified");
988          l->data=NULL;
989          omFree(d);
990          omFree(path);
991          omFree(cli_host);
992          return TRUE;
993        }
994        else if(r == 1)
995        {
996          WarnS("program not specified, using /usr/local/bin/Singular");
997          strcpy(path,"/usr/local/bin/Singular");
998        }
999        char* ssh_command = (char*)omAlloc(256);
1000        char* ser_host = (char*)omAlloc(64);
1001        gethostname(ser_host,64);
1002        sprintf(ssh_command,"ssh %s %s -q --batch --link=ssi --MPhost=%s --MPport=%d &",cli_host,path,ser_host,portno);
1003        //Print("client on %s started:%s\n",cli_host,path);
1004        omFree(path);
1005        omFree(cli_host);
1006        if (TEST_OPT_PROT) { Print("running >>%s<<\n",ssh_command); }
1007        system(ssh_command);
1008        omFree(ssh_command);
1009        omFree(ser_host);
1010        clilen = sizeof(cli_addr);
1011        newsockfd = si_accept(sockfd, (struct sockaddr *) &cli_addr, (socklen_t *)&clilen);
1012        if(newsockfd < 0)
1013        {
1014          WerrorS("ERROR on accept");
1015          l->data=NULL;
1016          omFree(d);
1017          return TRUE;
1018        }
1019        //PrintS("client accepted\n");
1020        d->fd_read = newsockfd;
1021        d->fd_write = newsockfd;
1022        d->f_read = s_open(newsockfd);
1023        SSI_BLOCK_CHLD;
1024        d->f_write = fdopen(newsockfd, "w");
1025        si_close(sockfd);
1026        SSI_UNBLOCK_CHLD;
1027        SI_LINK_SET_RW_OPEN_P(l);
1028        d->send_quit_at_exit=1;
1029        SSI_BLOCK_CHLD;
1030        fprintf(d->f_write,"98 %d %d %u %u\n",SSI_VERSION,MAX_TOK,si_opt_1,si_opt_2);
1031        SSI_UNBLOCK_CHLD;
1032      }
1033      // ----------------------------------------------------------------------
1034      else if(strcmp(mode,"connect")==0)
1035      {
1036        char* host = (char*)omAlloc(256);
1037        int sockfd, portno/*, n*/;
1038        struct sockaddr_in serv_addr;
1039        struct hostent *server;
1040
1041        si_sscanf(l->name,"%255[^:]:%d",host,&portno);
1042        //Print("connect to host %s, port %d\n",host,portno);mflush();
1043        if (portno!=0)
1044        {
1045          sockfd = socket(AF_INET, SOCK_STREAM, 0);
1046          if (sockfd < 0) { WerrorS("ERROR opening socket"); return TRUE; }
1047          server = gethostbyname(host);
1048          if (server == NULL) {  WerrorS("ERROR, no such host");  return TRUE; }
1049          memset((char *) &serv_addr, 0, sizeof(serv_addr));
1050          serv_addr.sin_family = AF_INET;
1051          memcpy((char *)&serv_addr.sin_addr.s_addr,
1052                (char *)server->h_addr,
1053                server->h_length);
1054          serv_addr.sin_port = htons(portno);
1055          if (si_connect(sockfd,(sockaddr*)&serv_addr,sizeof(serv_addr)) < 0)
1056          { Werror("ERROR connecting(errno=%d)",errno); return TRUE; }
1057          //PrintS("connected\n");mflush();
1058          d->f_read=s_open(sockfd);
1059          d->fd_read=sockfd;
1060          d->f_write=fdopen(sockfd,"w");
1061          d->fd_write=sockfd;
1062          SI_LINK_SET_RW_OPEN_P(l);
1063          d->send_quit_at_exit=1;
1064          omFree(host);
1065        }
1066        else
1067        {
1068          l->data=NULL;
1069          omFree(d);
1070          return TRUE;
1071        }
1072      }
1073      // ======================================================================
1074      else
1075      {
1076        // normal link to a file
1077        FILE *outfile;
1078        char *filename=l->name;
1079
1080        if(filename[0]=='>')
1081        {
1082          if (filename[1]=='>')
1083          {
1084            filename+=2;
1085            mode = "a";
1086          }
1087          else
1088          {
1089            filename++;
1090            mode="w";
1091          }
1092        }
1093        SSI_BLOCK_CHLD;
1094        outfile=myfopen(filename,mode);
1095        SSI_UNBLOCK_CHLD;
1096        if (outfile!=NULL)
1097        {
1098          if (strcmp(l->mode,"r")==0)
1099          {
1100            SSI_BLOCK_CHLD;
1101            fclose(outfile);
1102            d->f_read=s_open_by_name(filename);
1103            SSI_UNBLOCK_CHLD;
1104          }
1105          else
1106          {
1107            SSI_BLOCK_CHLD;
1108            d->f_write = outfile;
1109            fprintf(d->f_write,"98 %d %d %u %u\n",SSI_VERSION,MAX_TOK,si_opt_1,si_opt_2);
1110            SSI_UNBLOCK_CHLD;
1111          }
1112        }
1113        else
1114        {
1115          omFree(d);
1116          l->data=NULL;
1117          return TRUE;
1118        }
1119      }
1120    }
1121  }
1122
1123  return FALSE;
1124}
1125
1126//**************************************************************************/
1127BOOLEAN ssiPrepClose(si_link l)
1128{
1129  if (l!=NULL)
1130  {
1131    ssiInfo *d = (ssiInfo *)l->data;
1132    if (d!=NULL)
1133    {
1134      if (d->send_quit_at_exit)
1135      {
1136        SSI_BLOCK_CHLD;
1137        fputs("99\n",d->f_write);
1138        fflush(d->f_write);
1139        SSI_UNBLOCK_CHLD;
1140      }
1141      d->quit_sent=1;
1142    }
1143  }
1144  return FALSE;
1145}
1146
1147BOOLEAN ssiClose(si_link l)
1148{
1149  if (l!=NULL)
1150  {
1151    SI_LINK_SET_CLOSE_P(l);
1152    ssiInfo *d = (ssiInfo *)l->data;
1153    if (d!=NULL)
1154    {
1155      if ((d->send_quit_at_exit)
1156      && (d->quit_sent==0))
1157      {
1158        SSI_BLOCK_CHLD;
1159        fputs("99\n",d->f_write);
1160        fflush(d->f_write);
1161        SSI_UNBLOCK_CHLD;
1162      }
1163      if (d->r!=NULL) rKill(d->r);
1164      if ((d->pid!=0)
1165      && (si_waitpid(d->pid,NULL,WNOHANG)==0))
1166      {
1167        struct timespec t;
1168        t.tv_sec=0;
1169        t.tv_nsec=50000000; // <=50 ms
1170        int r=nanosleep(&t,NULL);
1171        if((r==0) && (si_waitpid(d->pid,NULL,WNOHANG)==0))
1172        {
1173          kill(d->pid,15);
1174          t.tv_sec=0;
1175          t.tv_nsec=10000000; // <=10 ms
1176          r=nanosleep(&t,NULL);
1177          if((r==0)&&(si_waitpid(d->pid,NULL,WNOHANG)==0))
1178          {
1179            kill(d->pid,9); // just to be sure
1180            si_waitpid(d->pid,NULL,0);
1181          }
1182        }
1183      }
1184      if (d->f_read!=NULL) s_close(d->f_read);
1185      if (d->f_write!=NULL) fclose(d->f_write);
1186      if ((strcmp(l->mode,"tcp")==0)
1187      || (strcmp(l->mode,"fork")==0))
1188      {
1189        link_list hh=ssiToBeClosed;
1190        if (hh!=NULL)
1191        {
1192          if (hh->l==l)
1193          {
1194             ssiToBeClosed=(link_list)hh->next;
1195             omFreeSize(hh,sizeof(link_struct));
1196          }
1197          else while(hh->next!=NULL)
1198          {
1199            link_list hhh=(link_list)hh->next;
1200            if (hhh->l==l)
1201            {
1202              hh->next=hhh->next;
1203              omFreeSize(hhh,sizeof(link_struct));
1204              break;
1205            }
1206            else
1207              hh=(link_list)hh->next;
1208          }
1209        }
1210      }
1211      omFreeSize((ADDRESS)d,(sizeof *d));
1212    }
1213    l->data=NULL;
1214  }
1215  return FALSE;
1216}
1217
1218//**************************************************************************/
1219leftv ssiRead1(si_link l)
1220{
1221  ssiInfo *d = (ssiInfo *)l->data;
1222  leftv res=(leftv)omAlloc0(sizeof(sleftv));
1223  int t=0;
1224  t=s_readint(d->f_read);
1225  //Print("got type %d\n",t);
1226  switch(t)
1227  {
1228    case 1:res->rtyp=INT_CMD;
1229           res->data=(char *)ssiReadInt(d->f_read);
1230           break;
1231    case 2:res->rtyp=STRING_CMD;
1232           res->data=(char *)ssiReadString(d);
1233           break;
1234    case 3:res->rtyp=NUMBER_CMD;
1235           res->data=(char *)ssiReadNumber(d);
1236           break;
1237    case 4:res->rtyp=BIGINT_CMD;
1238           res->data=(char *)ssiReadBigInt(d);
1239           break;
1240    case 15:
1241    case 5:{
1242             d->r=ssiReadRing(d);
1243             d->r->ref++;
1244             res->rtyp=RING_CMD;
1245             res->data=(char*)d->r;
1246             // we are in the top-level, so set the basering to d->r:
1247             ssiSetCurrRing(d->r);
1248             if (t==15) return ssiRead1(l);
1249           }
1250           break;
1251    case 6:res->rtyp=POLY_CMD;
1252           if (d->r==NULL) goto no_ring;
1253           res->data=(char*)ssiReadPoly(d);
1254           break;
1255    case 7:res->rtyp=IDEAL_CMD;
1256           if (d->r==NULL) goto no_ring;
1257           res->data=(char*)ssiReadIdeal(d);
1258           break;
1259    case 8:res->rtyp=MATRIX_CMD;
1260           if (d->r==NULL) goto no_ring;
1261           res->data=(char*)ssiReadMatrix(d);
1262           break;
1263    case 9:res->rtyp=VECTOR_CMD;
1264           if (d->r==NULL) goto no_ring;
1265           res->data=(char*)ssiReadPoly(d);
1266           break;
1267    case 10:res->rtyp=MODUL_CMD;
1268           if (d->r==NULL) goto no_ring;
1269           res->data=(char*)ssiReadIdeal(d);
1270           break;
1271    case 11:
1272           {
1273             res->rtyp=COMMAND;
1274             res->data=ssiReadCommand(l);
1275             int nok=res->Eval();
1276             if (nok) WerrorS("error in eval");
1277             break;
1278           }
1279    case 12: /*DEF_CMD*/
1280           {
1281             res->rtyp=0;
1282             res->name=(char *)ssiReadString(d);
1283             int nok=res->Eval();
1284             if (nok) WerrorS("error in name lookup");
1285             break;
1286           }
1287    case 13: res->rtyp=PROC_CMD;
1288             res->data=ssiReadProc(d);
1289             break;
1290    case 14: res->rtyp=LIST_CMD;
1291             res->data=ssiReadList(l);
1292             break;
1293    case 16: res->rtyp=NONE; res->data=NULL;
1294             break;
1295    case 17: res->rtyp=INTVEC_CMD;
1296             res->data=ssiReadIntvec(d);
1297             break;
1298    case 18: res->rtyp=INTMAT_CMD;
1299             res->data=ssiReadIntmat(d);
1300             break;
1301    case 19: res->rtyp=BIGINTMAT_CMD;
1302             res->data=ssiReadBigintmat(d);
1303             break;
1304    case 20: ssiReadBlackbox(res,l);
1305             break;
1306    // ------------
1307    case 98: // version
1308             {
1309                int n98_v,n98_m;
1310                BITSET n98_o1,n98_o2;
1311                n98_v=s_readint(d->f_read);
1312                n98_m=s_readint(d->f_read);
1313                n98_o1=s_readint(d->f_read);
1314                n98_o2=s_readint(d->f_read);
1315                if ((n98_v!=SSI_VERSION) ||(n98_m!=MAX_TOK))
1316                {
1317                  Print("incompatible versions of ssi: %d/%d vs %d/%d",
1318                                  SSI_VERSION,MAX_TOK,n98_v,n98_m);
1319                }
1320                #ifndef NDEBUG
1321                if (TEST_OPT_DEBUG)
1322                  Print("// opening ssi-%d, MAX_TOK=%d\n",n98_v,n98_m);
1323                #endif
1324                si_opt_1=n98_o1;
1325                si_opt_2=n98_o2;
1326                return ssiRead1(l);
1327             }
1328    case 99: ssiClose(l); m2_end(0);
1329    case 0: if (s_iseof(d->f_read))
1330            {
1331              ssiClose(l);
1332              res->rtyp=DEF_CMD;
1333              break;
1334            }
1335    default: Werror("not implemented (t:%d)",t);
1336             omFreeSize(res,sizeof(sleftv));
1337             res=NULL;
1338             break;
1339  }
1340  return res;
1341no_ring: WerrorS("no ring");
1342  omFreeSize(res,sizeof(sleftv));
1343  return NULL;
1344}
1345//**************************************************************************/
1346BOOLEAN ssiWrite(si_link l, leftv data)
1347{
1348  if(SI_LINK_W_OPEN_P(l)==0)
1349     if (slOpen(l,SI_LINK_OPEN|SI_LINK_WRITE,NULL)) return TRUE;
1350  ssiInfo *d = (ssiInfo *)l->data;
1351  d->level++;
1352  //FILE *fich=d->f;
1353  while (data!=NULL)
1354  {
1355    int tt=data->Typ();
1356    void *dd=data->Data();
1357
1358    switch(tt /*data->Typ()*/)
1359    {
1360          case NONE/* nothing*/:fputs("16 ",d->f_write);
1361                          break;
1362          case STRING_CMD: fputs("2 ",d->f_write);
1363                           ssiWriteString(d,(char *)dd);
1364                           break;
1365          case INT_CMD: fputs("1 ",d->f_write);
1366                        ssiWriteInt(d,(int)(long)dd);
1367                        break;
1368          case BIGINT_CMD:fputs("4 ",d->f_write);
1369                        ssiWriteBigInt(d,(number)dd);
1370                        break;
1371          case NUMBER_CMD:
1372                          if (d->r!=currRing)
1373                          {
1374                            fputs("15 ",d->f_write);
1375                            ssiWriteRing(d,currRing);
1376                            if (d->level<=1) fputc('\n',d->f_write);
1377                          }
1378                          fputs("3 ",d->f_write);
1379                          ssiWriteNumber(d,(number)dd);
1380                        break;
1381          case RING_CMD:fputs("5 ",d->f_write);
1382                        ssiWriteRing(d,(ring)dd);
1383                        break;
1384          case POLY_CMD:
1385          case VECTOR_CMD:
1386                        if (d->r!=currRing)
1387                        {
1388                          fputs("15 ",d->f_write);
1389                          ssiWriteRing(d,currRing);
1390                          if (d->level<=1) fputc('\n',d->f_write);
1391                        }
1392                        if(tt==POLY_CMD) fputs("6 ",d->f_write);
1393                        else             fputs("9 ",d->f_write);
1394                        ssiWritePoly(d,tt,(poly)dd);
1395                        break;
1396          case IDEAL_CMD:
1397          case MODUL_CMD:
1398          case MATRIX_CMD:
1399                        if (d->r!=currRing)
1400                        {
1401                          fputs("15 ",d->f_write);
1402                          ssiWriteRing(d,currRing);
1403                          if (d->level<=1) fputc('\n',d->f_write);
1404                        }
1405                        if(tt==IDEAL_CMD)       fputs("7 ",d->f_write);
1406                        else if(tt==MATRIX_CMD) fputs("8 ",d->f_write);
1407                        else                    fputs("10 ",d->f_write);
1408                        ssiWriteIdeal(d,tt,(ideal)dd);
1409                        break;
1410          case COMMAND:
1411                   fputs("11 ",d->f_write);
1412                   ssiWriteCommand(l,(command)dd);
1413                   break;
1414          case DEF_CMD: /* not evaluated stuff in quotes */
1415                   fputs("12 ",d->f_write);
1416                   ssiWriteString(d,data->Name());
1417                   break;
1418          case PROC_CMD:
1419                   fputs("13 ",d->f_write);
1420                   ssiWriteProc(d,(procinfov)dd);
1421                   break;
1422          case LIST_CMD:
1423                   fputs("14 ",d->f_write);
1424                   ssiWriteList(l,(lists)dd);
1425                   break;
1426          case INTVEC_CMD:
1427                   fputs("17 ",d->f_write);
1428                   ssiWriteIntvec(d,(intvec *)dd);
1429                   break;
1430          case INTMAT_CMD:
1431                   fputs("18 ",d->f_write);
1432                   ssiWriteIntmat(d,(intvec *)dd);
1433                   break;
1434          case BIGINTMAT_CMD:
1435                   fputs("19 ",d->f_write);
1436                   ssiWriteBigintmat(d,(bigintmat *)dd);
1437                   break;
1438          default:
1439            if (tt>MAX_TOK)
1440            {
1441              blackbox *b=getBlackboxStuff(tt);
1442              fputs("20 ",d->f_write);
1443              b->blackbox_serialize(b,dd,l);
1444            }
1445            else
1446            {
1447              Werror("not implemented (t:%d, rtyp:%d)",tt, data->rtyp);
1448              d->level=0;
1449              return TRUE;
1450            }
1451            break;
1452    }
1453    if (d->level<=1) { fputc('\n',d->f_write); fflush(d->f_write); }
1454    data=data->next;
1455  }
1456  d->level--;
1457  return FALSE;
1458}
1459
1460BOOLEAN ssiGetDump(si_link l);
1461BOOLEAN ssiDump(si_link l);
1462
1463si_link_extension slInitSsiExtension(si_link_extension s)
1464{
1465  s->Open=ssiOpen;
1466  s->Close=ssiClose;
1467  s->Kill=ssiClose;
1468  s->Read=ssiRead1;
1469  s->Read2=(slRead2Proc)NULL;
1470  s->Write=ssiWrite;
1471  s->Dump=ssiDump;
1472  s->GetDump=ssiGetDump;
1473
1474  s->Status=slStatusSsi;
1475  s->type="ssi";
1476  return s;
1477}
1478
1479const char* slStatusSsi(si_link l, const char* request)
1480{
1481  ssiInfo *d=(ssiInfo*)l->data;
1482  if (d==NULL) return "not open";
1483  if (((strcmp(l->mode,"fork")==0)
1484  ||(strcmp(l->mode,"tcp")==0)
1485  ||(strcmp(l->mode,"connect")==0))
1486  && (strcmp(request, "read") == 0))
1487  {
1488    fd_set  mask/*, fdmask*/;
1489    struct timeval wt;
1490    if (s_isready(d->f_read)) return "ready";
1491    loop
1492    {
1493      /* Don't block. Return socket status immediately. */
1494      wt.tv_sec  = 0;
1495      wt.tv_usec = 0;
1496
1497      FD_ZERO(&mask);
1498      FD_SET(d->fd_read, &mask);
1499      //Print("test fd %d\n",d->fd_read);
1500    /* check with select: chars waiting: no -> not ready */
1501      switch (si_select(d->fd_read+1, &mask, NULL, NULL, &wt))
1502      {
1503        case 0: /* not ready */ return "not ready";
1504        case -1: /*error*/      return "error";
1505        case 1: /*ready ? */    break;
1506      }
1507    /* yes: read 1 char*/
1508    /* if \n, check again with select else ungetc(c), ready*/
1509      int c=s_getc(d->f_read);
1510      //Print("try c=%d\n",c);
1511      if (c== -1) return "eof"; /* eof or error */
1512      else if (isdigit(c))
1513      { s_ungetc(c,d->f_read); return "ready"; }
1514      else if (c>' ')
1515      {
1516        Werror("unknown char in ssiLink(%d)",c);
1517        return "error";
1518      }
1519      /* else: next char */
1520    }
1521  }
1522  else if (strcmp(request, "read") == 0)
1523  {
1524    if (SI_LINK_R_OPEN_P(l) && (!s_iseof(d->f_read)) && (s_isready(d->f_read))) return "ready";
1525    else return "not ready";
1526  }
1527  else if (strcmp(request, "write") == 0)
1528  {
1529    if (SI_LINK_W_OPEN_P(l)) return "ready";
1530    else return "not ready";
1531  }
1532  else return "unknown status request";
1533}
1534
1535int slStatusSsiL(lists L, int timeout)
1536{
1537// input: L: a list with links of type
1538//           ssi-connect, ssi-fork, ssi-tcp, MPtcp-fork or MPtcp-launch.
1539//           Note: Not every entry in L must be set.
1540//        timeout: timeout for select in micro-seconds
1541//           or -1 for infinity
1542//           or 0 for polling
1543// returns: ERROR (via Werror): L has wrong elements or link not open
1544//           -2: select returns an error
1545//           -1: the read state of all links is eof
1546//           0:  timeout (or polling): none ready,
1547//           i>0: (at least) L[i] is ready
1548  si_link l;
1549  ssiInfo *d;
1550  #ifdef HAVE_MPSR
1551  MP_Link_pt dd;
1552  #endif
1553  int d_fd;
1554  fd_set  mask, fdmask;
1555  FD_ZERO(&fdmask);
1556  FD_ZERO(&mask);
1557  int max_fd=0; /* 1 + max fd in fd_set */
1558
1559  /* timeout */
1560  #ifdef HAVE_PSELECT
1561  struct timespec wt;
1562  struct timespec *wt_ptr=&wt;
1563  #else
1564  struct timeval wt;
1565  struct timeval *wt_ptr=&wt;
1566  #endif
1567  int startingtime = getRTimer()/TIMER_RESOLUTION;  // in seconds
1568  if (timeout== -1)
1569  {
1570    wt_ptr=NULL;
1571  }
1572  else
1573  {
1574    wt.tv_sec  = timeout / 1000000;
1575  #ifdef HAVE_PSELECT
1576    wt.tv_nsec = 1000 * (timeout % 1000000);
1577  #else
1578    wt.tv_usec = timeout % 1000000;
1579  #endif
1580  }
1581
1582  /* signal mask for pselect() */
1583  sigset_t sigmask;
1584  if(sigprocmask(SIG_SETMASK, NULL, &sigmask) < 0)
1585  {
1586    WerrorS("error in sigprocmask()");
1587    return -2;
1588  }
1589  if(sigaddset(&sigmask, SIGCHLD) < 0)
1590  {
1591    WerrorS("error in sigaddset()");
1592    return -2;
1593  }
1594
1595  /* auxiliary variables */
1596  int i;
1597  int j;
1598  int k;
1599  int s;
1600  char fdmaskempty;
1601
1602  /* check the links and fill in fdmask */
1603  /* check ssi links for ungetc_buf */
1604  for(i=L->nr; i>=0; i--)
1605  {
1606    if (L->m[i].Typ()!=DEF_CMD)
1607    {
1608      if (L->m[i].Typ()!=LINK_CMD)
1609      { WerrorS("all elements must be of type link"); return -2;}
1610      l=(si_link)L->m[i].Data();
1611      if(SI_LINK_OPEN_P(l)==0)
1612      { WerrorS("all links must be open"); return -2;}
1613      if (((strcmp(l->m->type,"ssi")!=0) && (strcmp(l->m->type,"MPtcp")!=0))
1614      || ((strcmp(l->mode,"fork")!=0) && (strcmp(l->mode,"tcp")!=0)
1615        && (strcmp(l->mode,"launch")!=0) && (strcmp(l->mode,"connect")!=0)))
1616      {
1617        WerrorS("all links must be of type ssi:fork, ssi:tcp, ssi:connect,");
1618        WerrorS(" MPtcp:fork or MPtcp:launch");
1619        return -2;
1620      }
1621    #ifdef HAVE_MPSR
1622      if (strcmp(l->m->type,"ssi")==0)
1623      {
1624        d=(ssiInfo*)l->data;
1625        d_fd=d->fd_read;
1626        if (!s_isready(d->f_read))
1627        {
1628          FD_SET(d_fd, &fdmask);
1629          if (d_fd > max_fd) max_fd=d_fd;
1630        }
1631        else
1632          return i+1;
1633      }
1634      else
1635      {
1636        dd=(MP_Link_pt)l->data;
1637        d_fd=((MP_TCP_t *)dd->transp.private1)->sock;
1638        FD_SET(d_fd, &fdmask);
1639        if (d_fd > max_fd) max_fd=d_fd;
1640      }
1641    #else
1642      d=(ssiInfo*)l->data;
1643      d_fd=d->fd_read;
1644      if (!s_isready(d->f_read))
1645      {
1646        FD_SET(d_fd, &fdmask);
1647        if (d_fd > max_fd) max_fd=d_fd;
1648      }
1649      else
1650        return i+1;
1651    #endif
1652    }
1653  }
1654  max_fd++;
1655
1656do_select:
1657  /* copy fdmask to mask */
1658  FD_ZERO(&mask);
1659  for(k = 0; k < max_fd; k++)
1660  {
1661    if(FD_ISSET(k, &fdmask))
1662    {
1663      FD_SET(k, &mask);
1664    }
1665  }
1666
1667  /* check with select: chars waiting: no -> not ready */
1668  #ifdef HAVE_SIMPLEIPC
1669  sipc_semaphore_release(0);
1670  #endif
1671  #ifdef HAVE_PSELECT
1672  s = si_pselect(max_fd, &mask, NULL, NULL, wt_ptr, &sigmask);
1673  #else
1674  SSI_BLOCK_CHLD;
1675  s = si_select(max_fd, &mask, NULL, NULL, wt_ptr);
1676  SSI_UNBLOCK_CHLD;
1677  #endif
1678  #ifdef HAVE_SIMPLEIPC
1679  sipc_semaphore_acquire(0);
1680  #endif
1681
1682  if (s==-1)
1683  {
1684    WerrorS("error in select call");
1685    return -2; /*error*/
1686  }
1687  if (s==0)
1688  {
1689    return 0; /*poll: not ready */
1690  }
1691  else /* s>0, at least one ready  (the number of fd which are ready is s)*/
1692  {
1693    j=0;
1694    while (j<=max_fd) { if (FD_ISSET(j,&mask)) break; j++; }
1695    for(i=L->nr; i>=0; i--)
1696    {
1697      if (L->m[i].rtyp==LINK_CMD)
1698      {
1699        l=(si_link)L->m[i].Data();
1700        #ifdef HAVE_MPSR
1701        if (strcmp(l->m->type,"ssi")!=0)
1702        {
1703          // for MP links, return here:
1704          dd=(MP_Link_pt)l->data;
1705          d_fd=((MP_TCP_t *)dd->transp.private1)->sock;
1706          if(j==d_fd) return i+1;
1707        }
1708        else
1709        {
1710          d=(ssiInfo*)l->data;
1711          d_fd=d->fd_read;
1712          if(j==d_fd) break;
1713        }
1714        #else
1715        d=(ssiInfo*)l->data;
1716        d_fd=d->fd_read;
1717        if(j==d_fd) break;
1718        #endif
1719      }
1720    }
1721    // only ssi links:
1722    loop
1723    {
1724      /* yes: read 1 char*/
1725      /* if \n, check again with select else ungetc(c), ready*/
1726      /* setting: d: current ssiInfo, j current fd, i current entry in L*/
1727      int c=s_getc(d->f_read);
1728      //Print("try c=%d\n",c);
1729      if (c== -1) /* eof */
1730      {
1731        FD_CLR(j,&fdmask);
1732        fdmaskempty = 1;
1733        for(k = 0; k < max_fd; k++)
1734        {
1735          if(FD_ISSET(k, &fdmask))
1736          {
1737            fdmaskempty = 0;
1738            break;
1739          }
1740        }
1741        if(fdmaskempty)
1742        {
1743          return -1;
1744        }
1745        if(timeout != -1)
1746        {
1747          timeout = si_max(0,
1748             timeout - 1000000*(getRTimer()/TIMER_RESOLUTION - startingtime));
1749          wt.tv_sec  = timeout / 1000000;
1750          #ifdef HAVE_PSELECT
1751          wt.tv_nsec = 1000 * (timeout % 1000000);
1752          #else
1753          wt.tv_usec = (timeout % 1000000);
1754          #endif
1755        }
1756        goto do_select;
1757      }
1758
1759      else if (isdigit(c))
1760      { s_ungetc(c,d->f_read); return i+1; }
1761      else if (c>' ')
1762      {
1763        Werror("unknown char in ssiLink(%d)",c);
1764        return -2;
1765      }
1766      /* else: next char */
1767    }
1768  }
1769}
1770
1771int ssiBatch(const char *host, const char * port)
1772/* return 0 on success, >0 else*/
1773{
1774  si_link l=(si_link) omAlloc0Bin(sip_link_bin);
1775  char *buf=(char*)omAlloc(256);
1776  sprintf(buf,"ssi:connect %s:%s",host,port);
1777  slInit(l, buf);
1778  if (slOpen(l,SI_LINK_OPEN,NULL)) return 1;
1779  SI_LINK_SET_RW_OPEN_P(l);
1780
1781  idhdl id = enterid(omStrDup("link_ll"), 0, LINK_CMD, &IDROOT, FALSE);
1782  IDLINK(id) = l;
1783
1784  loop
1785  {
1786    leftv h=ssiRead1(l); /*contains an exit.... */
1787    if (feErrors != NULL && *feErrors != '\0')
1788    {
1789      // handle errors:
1790      PrintS(feErrors); /* currently quite simple */
1791      *feErrors = '\0';
1792    }
1793    ssiWrite(l,h);
1794    h->CleanUp();
1795    omFreeBin(h, sleftv_bin);
1796  }
1797  /* never reached*/
1798  exit(0);
1799}
1800
1801static int ssiReserved_P=0;
1802static int ssiReserved_sockfd;
1803static  struct sockaddr_in ssiResverd_serv_addr;
1804static int  ssiReserved_Clients;
1805int ssiReservePort(int clients)
1806{
1807  if (ssiReserved_P!=0)
1808  {
1809    WerrorS("ERROR already a reverved port requested");
1810    return 0;
1811  }
1812  int portno;
1813  // int n;
1814  ssiReserved_sockfd = socket(AF_INET, SOCK_STREAM, 0);
1815  if(ssiReserved_sockfd < 0)
1816  {
1817    WerrorS("ERROR opening socket");
1818    return 0;
1819  }
1820  memset((char *) &ssiResverd_serv_addr,0, sizeof(ssiResverd_serv_addr));
1821  portno = 1025;
1822  ssiResverd_serv_addr.sin_family = AF_INET;
1823  ssiResverd_serv_addr.sin_addr.s_addr = INADDR_ANY;
1824  do
1825  {
1826    portno++;
1827    ssiResverd_serv_addr.sin_port = htons(portno);
1828    if(portno > 50000)
1829    {
1830      WerrorS("ERROR on binding (no free port available?)");
1831      return 0;
1832    }
1833  }
1834  while(bind(ssiReserved_sockfd, (struct sockaddr *) &ssiResverd_serv_addr, sizeof(ssiResverd_serv_addr)) < 0);
1835  ssiReserved_P=portno;
1836  listen(ssiReserved_sockfd,clients);
1837  ssiReserved_Clients=clients;
1838  return portno;
1839}
1840
1841extern si_link_extension si_link_root;
1842si_link ssiCommandLink()
1843{
1844  if (ssiReserved_P==0)
1845  {
1846    WerrorS("ERROR no reverved port requested");
1847    return NULL;
1848  }
1849  struct sockaddr_in cli_addr;
1850  int clilen = sizeof(cli_addr);
1851  int newsockfd = si_accept(ssiReserved_sockfd, (struct sockaddr *) &cli_addr, (socklen_t *)&clilen);
1852  if(newsockfd < 0)
1853  {
1854    Werror("ERROR on accept (errno=%d)",errno);
1855    return NULL;
1856  }
1857  si_link l=(si_link) omAlloc0Bin(sip_link_bin);
1858  si_link_extension s = si_link_root;
1859  si_link_extension prev = s;
1860  while (strcmp(s->type, "ssi") != 0)
1861  {
1862    if (s->next == NULL)
1863    {
1864      prev = s;
1865      s = NULL;
1866      break;
1867    }
1868    else
1869    {
1870      s = s->next;
1871    }
1872  }
1873  if (s != NULL)
1874    l->m = s;
1875  else
1876  {
1877    si_link_extension ns = (si_link_extension)omAlloc0Bin(s_si_link_extension_bin);
1878    prev->next=slInitSsiExtension(ns);
1879    l->m = prev->next;
1880  }
1881  l->name=omStrDup("");
1882  l->mode=omStrDup("tcp");
1883  l->ref=1;
1884  ssiInfo *d=(ssiInfo*)omAlloc0(sizeof(ssiInfo));
1885  sigprocmask(SIG_SETMASK, NULL, &ssi_sigmask);
1886  sigaddset(&ssi_sigmask, SIGCHLD);
1887  l->data=d;
1888  d->fd_read = newsockfd;
1889  d->fd_write = newsockfd;
1890  d->f_read = s_open(newsockfd);
1891  d->f_write = fdopen(newsockfd, "w");
1892  SI_LINK_SET_RW_OPEN_P(l);
1893  ssiReserved_Clients--;
1894  if (ssiReserved_Clients<=0)
1895  {
1896    ssiReserved_P=0;
1897    si_close(ssiReserved_sockfd);
1898  }
1899  return l;
1900}
1901/*---------------------------------------------------------------------*/
1902/**
1903 * @brief additional default signal handler
1904
1905  // some newer Linux version cannot have SIG_IGN for SIGCHLD,
1906  // so use this nice routine here:
1907  //  SuSe 9.x reports -1 always
1908  //  Redhat 9.x/FC x reports sometimes -1
1909  // see also: hpux_system
1910  // also needed by getrusage (timer etc.)
1911
1912 @param[in] sig
1913**/
1914/*---------------------------------------------------------------------*/
1915void sig_chld_hdl(int /*sig*/)
1916{
1917  pid_t kidpid;
1918  int status;
1919
1920  loop
1921  {
1922    kidpid = si_waitpid(-1, &status, WNOHANG);
1923    if (kidpid==-1)
1924    {
1925      /* continue on interruption (EINTR): */
1926      if (errno == EINTR) continue;
1927      /* break on anything else (EINVAL or ECHILD according to manpage): */
1928      break;
1929    }
1930    else if (kidpid==0) break; /* no more children to process, so break */
1931
1932    //printf("Child %ld terminated\n", kidpid);
1933    link_list hh=ssiToBeClosed;
1934    while(hh!=NULL)
1935    {
1936      if((hh->l!=NULL) && (hh->l->m->Open==ssiOpen))
1937      {
1938        ssiInfo *d = (ssiInfo *)hh->l->data;
1939        if(d->pid==kidpid)
1940        {
1941          if(ssiToBeClosed_inactive)
1942          {
1943            ssiToBeClosed_inactive=FALSE;
1944            slClose(hh->l);
1945            ssiToBeClosed_inactive=TRUE;
1946            break;
1947          }
1948          else break;
1949        }
1950        else hh=(link_list)hh->next;
1951      }
1952      else hh=(link_list)hh->next;
1953    }
1954  }
1955}
1956
1957static BOOLEAN DumpSsiIdhdl(si_link l, idhdl h)
1958{
1959  int type_id = IDTYP(h);
1960
1961  // C-proc not to be dumped, also LIB-proc not
1962  if (type_id == PROC_CMD)
1963  {
1964    if (IDPROC(h)->language == LANG_C) return FALSE;
1965    if (IDPROC(h)->libname != NULL) return FALSE;
1966  }
1967  // do not dump links
1968  if (type_id == LINK_CMD) return FALSE;
1969
1970  // do not dump ssi internal rings: ssiRing*
1971  if ((type_id == RING_CMD) && (strncmp(IDID(h),"ssiRing",7)==0))
1972    return FALSE;
1973
1974  command D=(command)omAlloc0(sizeof(*D));
1975  sleftv tmp;
1976  memset(&tmp,0,sizeof(tmp));
1977  tmp.rtyp=COMMAND;
1978  tmp.data=D;
1979
1980  if (type_id == PACKAGE_CMD)
1981  {
1982    // do not dump Top
1983    if (strcmp(IDID(h), "Top") == 0) return FALSE;
1984    package p=(package)IDDATA(h);
1985    // dump Singular-packages as load("...");
1986    if (p->language==LANG_SINGULAR)
1987    {
1988      D->op=LOAD_CMD;
1989      D->argc=1;
1990      D->arg1.rtyp=STRING_CMD;
1991      D->arg1.data=p->libname;
1992      ssiWrite(l,&tmp);
1993      omFree(D);
1994      return FALSE;
1995    }
1996  }
1997
1998  // handle qrings separately
1999  //if (type_id == QRING_CMD)
2000  //  return DumpSsiQringQring(l, h);
2001
2002  // put type and name
2003  //Print("generic dump:%s,%s\n",IDID(h),Tok2Cmdname(IDTYP(h)));
2004  D->op='=';
2005  D->argc=2;
2006  D->arg1.rtyp=DEF_CMD;
2007  D->arg1.name=IDID(h);
2008  D->arg2.rtyp=IDTYP(h);
2009  D->arg2.data=IDDATA(h);
2010  ssiWrite(l,&tmp);
2011  omFree(D);
2012  return FALSE;
2013}
2014static BOOLEAN ssiDumpIter(si_link l, idhdl h)
2015{
2016  if (h == NULL) return FALSE;
2017
2018  if (ssiDumpIter(l, IDNEXT(h))) return TRUE;
2019
2020  // need to set the ring before writing it, otherwise we get in
2021  // trouble with minpoly
2022  if (IDTYP(h) == RING_CMD || IDTYP(h) == QRING_CMD)
2023    rSetHdl(h);
2024
2025  if (DumpSsiIdhdl(l, h)) return TRUE;
2026
2027  // do not dump ssi internal rings: ssiRing*
2028  // but dump objects of all other rings
2029  if ((IDTYP(h) == RING_CMD || IDTYP(h) == QRING_CMD)
2030  && (strncmp(IDID(h),"ssiRing",7)!=0))
2031    return ssiDumpIter(l, IDRING(h)->idroot);
2032  else
2033    return FALSE;
2034}
2035BOOLEAN ssiDump(si_link l)
2036{
2037  idhdl h = IDROOT, rh = currRingHdl;
2038  BOOLEAN status = ssiDumpIter(l, h);
2039
2040  //if (! status ) status = DumpAsciiMaps(fd, h, NULL);
2041
2042  if (currRingHdl != rh) rSetHdl(rh);
2043  //fprintf(fd, "option(set, intvec(%d, %d));\n", si_opt_1, si_opt_2);
2044
2045  return status;
2046}
2047BOOLEAN ssiGetDump(si_link l)
2048{
2049  ssiInfo *d=(ssiInfo*)l->data;
2050  loop
2051  {
2052    if (!SI_LINK_OPEN_P(l)) break;
2053    if (s_iseof(d->f_read)) break;
2054    leftv h=ssiRead1(l); /*contains an exit.... */
2055    if (feErrors != NULL && *feErrors != '\0')
2056    {
2057      // handle errors:
2058      PrintS(feErrors); /* currently quite simple */
2059      return TRUE;
2060      *feErrors = '\0';
2061    }
2062    h->CleanUp();
2063    omFreeBin(h, sleftv_bin);
2064  }
2065  return FALSE;
2066}
2067// ----------------------------------------------------------------
2068// format
2069// 1 int %d
2070// 2 string <len> %s
2071// 3 number
2072// 4 bigint 4 %d or 3 <mpz_t>
2073// 5 ring
2074// 6 poly
2075// 7 ideal
2076// 8 matrix
2077// 9 vector
2078// 10 module
2079// 11 command
2080// 12 def <len> %s
2081// 13 proc <len> %s
2082// 14 list %d <elem1> ....
2083// 15 setring .......
2084// 16 nothing
2085// 17 intvec <len> ...
2086// 18 intmat
2087// 19 bigintmat <r> <c> ...
2088//
2089// 20 blackbox <name> ...
2090//
2091// 98: verify version: <ssi-version> <MAX_TOK> <OPT1> <OPT2>
2092// 99: quit Singular
Note: See TracBrowser for help on using the repository browser.