source: git/Singular/links/ssiLink.cc @ 868d77d

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