source: git/Singular/links/ssiLink.cc @ 2ea186

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