/*                                                      SCOT.C       */
/* aldel Jul 91 */


#include "scot.h"
#include "text.h"

static char curline[MAXLINE+1]; /* current line of infile */
static int inx,                 /* column # */
           iny,                 /* line # */
           errcount;            /* errors so far */
static FILE *infile,*outfile;
static char *infilename;

static int naturals[PITCHCLASSES]={0,2,4,5,7,9,11};

static Macro *gmac;             /* global macro list */

int scot(FILE *inf,FILE *outf, char *fil) /* main externally-visible procedure */
{
    char s[128];
    Inst *insttop,*ip;

    initf(inf,outf,fil);
    if (findword(s) || strcmp(s,"orchestra"))
      scotferror(Str(X_456,"Score must start with orchestra section"));
    readorch(&insttop);
    for (;;) {
      if (findword(s)) break;
      if (!strcmp(s,"functions")) readfunctions();
      else if (!strcmp(s,"score")) readscore(insttop);
      else scotferror(Str(X_261,"Expected score or functions section"));
    }
    fputs("e\n",outfile);
    while (insttop) {
      ip=insttop;
      insttop=insttop->next;
      free(ip->name);
      free((char *)ip);
    }
    if (errcount) reporterrcount();
    return errcount;
}


static                 /* reads from one $instrument to the next */
void readinstsec(
    Inst *inst,
    Nextp **nextlist,
    Rat *grpmul,
    Rat *timesig,
    Rat *curtime,
    Rat *lastbar,
    Rat *lastnote,
    Note **notetop,
    Note **ln,
    Tempo **tempop,
    int *accidentals,
    int *octaves,
    int *vertical,
    int *key,
    int *barkey,
    int *transpose,
    char *endchar)
{
    static Rat durdiv={4L,1L};

    int c,z,lastpitchclass;
    char s[128],*sp;
    Rat ratstack,rattmp;
    Note *pn,*nn,*pn2=NULL;
    Strlist *ps;
    Nextp *nextpp;

#ifdef DEBUG
    printf("Reading instrument section: %s\n",inst->name);
#endif

    pn = (*notetop);
    for (;;) {
      findchar(&c);
      if (strchr(endchar,c)) break;

#ifdef DEBUG
      printf("Processing char: %c\n",c);
#endif

      switch (c) {
      case 't':
        if (findint(&c)) {
          scoterror(Str(X_487,"Tempo must be specified"));
          break;
        }
        if ((*tempop)->next) {
          scoterror(Str(X_442,"Redefinition of tempo"));
          break;
        }
        (*tempop)->next = (Tempo *) malloc(sizeof(Tempo));
        *tempop = (*tempop)->next;
        (*tempop)->next = NULL;
        ratass(&((*tempop)->time),curtime);
        (*tempop)->val = c;
        break;
      case '!':
        efindword(s);
        if ((c=strlen(s))<2)
          scoterror(Str(X_350,"Must specify 2 or more letters of keyword"));
        if (!strncmp(s,"accidentals",c)) {
          if (findonoff(accidentals))
            scoterror(Str(X_349,"Must be \"on\" or \"off\""));

#ifdef DEBUG
          printf(" accidentals %s\n",accidentals ? "on" : "off");
#endif

        }
        else if (!strncmp(s, "octaves",c)) {
          if (findonoff(octaves))
            scoterror(Str(X_349,"Must be \"on\" or \"off\""));

#ifdef DEBUG
          printf(" ocatves %s\n",*octaves ? "on" : "off");
#endif

        }
        else if (!strncmp(s,"vertical",c)) {
          if (findonoff(vertical))
            scoterror(Str(X_349,"Must be \"on\" or \"off\""));

#ifdef DEBUG
          printf(" vertical %s\n",*vertical ? "on" : "off");
#endif

        }
        else if (!strncmp(s,"timesignature",c)) {
          efindword(s);
          if ((sscanf(s,"%lu/%lu",&timesig->num,&timesig->denom)!=2) ||
              (&timesig->denom==0)) {
            scoterror(Str(X_318,"Invalid time signature"));
            timesig->num=0;
            timesig->denom=1;
          }

#ifdef DEBUG
          printf(" time sig=%lu/%lu\n",timesig->num,timesig->denom);
#endif

          ratstack.num=4; ratstack.denom=1;
          ratmul(timesig,timesig,&ratstack);

#ifdef DEBUG
          printf(" measure length=%f\n",ratval(timesig));
#endif

        }
        else if (!strncmp(s,"key",c)) {
          int y;

          efindword(s);
          for (z=0;z<PITCHCLASSES;z++) key[z]=0;
          c=y=0;
          for (z=0;s[z]!=(char) 0;z++)
            switch ((int) s[z]) {
            case '#':
              c=y+1;
              y++;
              break;
            case '-':
              c=y-1;
              y--;
              break;
            default:
              if (!isalpha(s[z])) scoterror(Str(X_193,"Bad key signature"));
              key[letterval((int) s[z])]=c;
              y=0;
            }
          for (z=0;z<PITCHCLASSES;z++) barkey[z]=key[z];
        }
        else if (!strncmp(s,"transpose",c)) {
          efindword(s);
          *transpose=0;
          for (z=0;s[z];z++) {
            switch (s[z]) {
            case ',':
              (*transpose)-=NOTESPEROCT;
              break;
            case '\'':
              (*transpose)+=NOTESPEROCT;
              break;
            case '=':
              (*transpose)=0;
              break;
            case '#':
              (*transpose)++;
              break;
            case '-':
              (*transpose)--;
              break;
            default:
              (*transpose)+=naturals[letterval((int) s[z])];
            }
          }
        }
        else if (!strncmp(s,"next",c)) {
          efindword(s);
          if (sscanf(s,"p%d",&c)!=1) {
            scoterror(Str(X_314,"Invalid field"));
            efindword(s);
            break;
          }
          efindword(s);
          if (sscanf(s,"p%d",&z)!=1) {
            scoterror(Str(X_314,"Invalid field"));
            break;
          }
          if (*nextlist==NULL) {
            *nextlist=(Nextp *) malloc(sizeof(Nextp));
            nextpp=(*nextlist);
            nextpp->next=NULL;
          }
          else {
            nextpp=(*nextlist);
            if ((c==nextpp->dst) || (z==nextpp->src))
              scoterror(Str(X_357,"Nested next-parameter passing"));
            while (nextpp->next) {
              nextpp=nextpp->next;
              if ((c==nextpp->dst) || (z==nextpp->src))
                scoterror(Str(X_357,"Nested next-parameter passing"));
            }
            nextpp->next=(Nextp *) malloc(sizeof(Nextp));
            nextpp=nextpp->next;
            nextpp->next=NULL;
          }
          nextpp->src=c;
          nextpp->dst=z;
        }
        else scoterror(Str(X_515,"Unrecognised keyword"));
        break;
      case '{':
        findint(&c);
        expectchar(':');
        if (!c) {
          ratstack.num=2L;
          ratstack.denom=3L;
        }
        else {
          ratstack.denom=(unsigned long) c;
          findint(&c);
          if (!c) {
            for (z=1; (unsigned long) z<ratstack.denom;z*=2);
            z /= 2;
            ratstack.num=(unsigned long) z;
          }
          else ratstack.num=(unsigned long) c;
          expectchar(':');
        }
        ratmul(grpmul,grpmul,&ratstack);
        readinstsec(inst,nextlist,grpmul,timesig,curtime,
                    lastbar,lastnote,notetop,ln,tempop,accidentals,octaves,
                    vertical,key,barkey,transpose,":");
        ratdiv(grpmul,grpmul,&ratstack);
        expectchar(':');
        expectchar('}');
        break;
      case '(':
        ratass(&ratstack,curtime);
        if (pn==(*notetop)) {
          readinstsec(inst,nextlist,grpmul,timesig,curtime,
                      lastbar,lastnote,notetop,ln,tempop,accidentals,octaves,
                      vertical,key,barkey,transpose,")");
          pn=(*notetop);
        }
        else {
          readinstsec(inst,nextlist,grpmul,timesig,curtime,
                      lastbar,lastnote,&pn2->next,ln,tempop,accidentals,octaves,
                      vertical,key,barkey,transpose,")");
          pn=pn2->next;
        }
        expectchar(')');
        ratass(lastnote,&ratstack);
        break;
      case '/':
        ratadd(lastbar,lastbar,timesig);
        if ((timesig->num) && (ratcmp(lastbar,curtime))) {
          scoterror(Str(X_540,"Wrong number of beats in bar"));
          ratass(lastbar,curtime);
        }
        for (z=0;z<PITCHCLASSES;z++) barkey[z]=key[z];
        break;
      case '<':
        if (pn==NULL) {
          scoterror(Str(X_475,"Syntax error: cannot back up"));
          break;
        }
        if (pn->next==NULL) {
          pn->next=(Note *) malloc(sizeof(Note));
          initnote(pn->next);
          pn->next->instrum=pn->instrum+0.01;
        }
        pn2=pn;
        pn=pn->next;
        ratass(curtime,lastnote);
        break;
      default:

#ifdef DEBUG
        printf("Reading note\n");
        printf(" time=%lu/%lu\n",curtime->num,curtime->denom);
        printf(" =%f\n",ratval(curtime));
#endif

        scotungetc();
        nn=(Note *) malloc(sizeof(Note));
        nn->p=NULL;
        nn->written=FALSE;
        if (*notetop==NULL) {
          pn=(*ln)=(*notetop)=(Note *) malloc(sizeof(Note));
          initnote(*notetop);
          (*notetop)->instrum=(double) inst->number+0.01;
        }
        else if (ratcmp(curtime,lastnote)) pn=(*notetop);
        nn->instrum=pn->instrum;

#ifdef DEBUG
        printf(" instrument #%f\n",nn->instrum);
#endif

        if (*vertical) strlistcopy(&nn->carryp,&(*ln)->carryp);
        else strlistcopy(&nn->carryp,&pn->carryp);
        for (nextpp=(*nextlist);nextpp;nextpp=nextpp->next) {
          sp=findparam(nextpp->dst,&nn->carryp);
          if (!strcmp(sp,".")) strcpy(sp,NEXTP);
        }
        ratass(&nn->start,curtime);
        if (!findint(&c)) {
          ratstack.num=(unsigned long) c;
          ratstack.denom=1L;
          ratdiv(&nn->dur,&durdiv,&ratstack);
          ratass(&ratstack,&nn->dur);
          rattmp.num=1L; rattmp.denom=2L;
          for (;;) {
            findchar(&c);
            if (c!='.') break;
            ratmul(&ratstack,&ratstack,&rattmp);
            ratadd(&nn->dur,&nn->dur,&ratstack);
          }
        }
        else {
          if (*vertical) ratass(&nn->dur,&((*ln)->lastdur));
          else ratass(&nn->dur,&pn->lastdur);
          findchar(&c);
        }
        ratass(&nn->lastdur,&nn->dur);
        ratmul(&nn->dur,&nn->dur,grpmul);

#ifdef DEBUG
        printf(" duration=%f\n",ratval(&nn->dur));
        printf(" c=%c\n",c);
#endif

        if (c=='=') {
          nn->octave=8;
          lastpitchclass=0;
        }
        else {
          nn->octave=pn->octave;
          lastpitchclass=pn->pitchclass;
          scotungetc();
        }
        for (;;) {
          findchar(&c);
          if (c=='\'') nn->octave++;
          else if (c==',') nn->octave--;
          else break;
        }
        if (c=='r') {
          ratass(lastnote,curtime);
          ratmul(&rattmp,&nn->lastdur,grpmul);
          ratadd(curtime,curtime,&rattmp);
          ratass(&(*ln)->lastdur,&nn->lastdur);
          ratass(&pn->lastdur,&nn->lastdur);
          freenote(nn);
          break;
        }
        else {
          nn->pitchclass=letterval(c);
          if (*octaves) {
            c=nn->pitchclass-lastpitchclass;
            if (c<-(PITCHCLASSES/2)) nn->octave++;
            else if (c>PITCHCLASSES/2) nn->octave--;
          }
        }
        nn->accid=0;
        nn->accmod=FALSE;
        for (;;) {
          findchar(&c);
          if (c=='#') {
            nn->accid++;
            nn->accmod=TRUE;
          }
          else if (c=='-') {
            nn->accid--;
            nn->accmod=TRUE;
          }
          else if (c=='n') {
            nn->accid=0;
            nn->accmod=TRUE;
          }
          else break;
        }
        if (!nn->accmod) nn->accid=barkey[nn->pitchclass];
        else if (*accidentals) barkey[nn->pitchclass]=nn->accid;

#ifdef DEBUG
        printf(" transpose=%d\n",*transpose);
        printf(" octave=%d pitchclass=%d accid=%d transpose=%d pitch=%f\n",
               nn->octave,nn->pitchclass,nn->accid,*transpose,
               pitchval(nn->octave,nn->pitchclass,nn->accid,*transpose));
#endif

        if (c=='_') {
          findchar(&c);
          if (c=='_') {
            nn->tie=TRUE;
            nn->slur=0;
            findchar(&c);
          }
          else {
            nn->slur=1;
            nn->tie=FALSE;
          }
        }
        else {
          nn->slur=0;
          nn->tie=FALSE;
        }
        if (pn->slur & 1) nn->slur+=2;

#ifdef DEBUG
        printf(" slur=%d tie=%d\n",nn->slur,nn->tie);
#endif

        if (pn->tie) {
          ratadd(&rattmp,&pn->start,&pn->dur);
          if (ratcmp(&rattmp,curtime)) scoterror(Str(X_304,"Improper tie"));
          if (((nn->octave != pn->octave) ||
               (nn->pitchclass != pn->pitchclass) ||
               ((nn->accid != pn->accid) && (nn->accmod))) &&
              (pitchval(nn->octave,nn->pitchclass,nn->accid,*transpose) !=
               pitchval(pn->octave,pn->pitchclass,pn->accid,*transpose)))
            scoterror(Str(X_491,"Tie between different pitches"));
          ratadd(&pn->dur,&pn->dur,&nn->dur);
          ratass(&pn->lastdur,&nn->lastdur);
          pn->slur+=nn->slur;
          pn->tie=nn->tie;
          freenote(nn);
          nn=pn;
          if (c==(char) '[') scoterror(Str(X_534,"Warning: params changed on tie"));
        }
        else {
          ps=nn->p=(Strlist *) malloc(sizeof(Strlist));
          for (z=0;z<4;z++) {
            ps->next=(Strlist *) malloc(sizeof(Strlist));
            ps=ps->next;
          }
          ps->next=NULL;
        }
        ps=nn->p;
        sprintf(ps->str,"%.02f",nn->instrum);
        ps=ps->next;
        sprintf(ps->str,"%g",ratval(&nn->start));
        ps=ps->next;
        sprintf(ps->str,"%g",ratval(&nn->dur));
        ps=ps->next;
        sprintf(ps->str,"%d",nn->slur);
        ps=ps->next;
        sprintf(ps->str,"%.02f",
                pitchval(nn->octave,nn->pitchclass,nn->accid,*transpose));
        if (c=='[') {
          char *pars;
          int pnum;

          pars=readparams(inst);

#ifdef DEBUG
          printf("Params: %s\n",pars);
#endif

          z=0;
          pnum=6;
          while (strchr(" \t\r\n",(int) pars[z])) z++;
          for (;;) {
            if (pars[z]==(char) ']') break;
            c=0;
            while (!strchr(" \t\r\n:]",(int) pars[z])) s[c++]=pars[z++];
            s[c]=(char) 0;

#ifdef DEBUG
            printf("Read: %s\n",s);
#endif

            while (strchr(" \t\r\n",(int) pars[z])) z++;
            if (pars[z]==(char) ':') {
              pnum=atoi(s);
              if (pnum<6) scoterror(Str(X_422,"Parameter number out of range"));
              z++;
              while (strchr(" \t\r\n",(int) pars[z])) z++;
              continue;
            }

#ifdef DEBUG
            printf("Param #%d: %s\n",pnum,s);
#endif

            if (s[0]==(char) '\'') {
              addparam(pnum,&s[1],&nn->p);
              addparam(pnum,".",&nn->carryp);
            }
            else {
              addparam(pnum,s,&nn->p);
              addparam(pnum,s,&nn->carryp);
            }
            pnum++;
          }
          free(pars);
        }
        else scotungetc();
        if ((nn!=pn) && (!pn->written)) {

#ifdef DEBUG
          printf("  doing nextp stuff:\n");
#endif

          for (nextpp=(*nextlist);nextpp;nextpp=nextpp->next) {

#ifdef DEBUG
            printf("   carrying p%d to p%d?\n",nextpp->src,nextpp->dst);
#endif

            if (!strcmp(findparam(nextpp->dst,&pn->carryp),NEXTP)) {
              sp=findparam(nextpp->dst,&pn->p);
              if (!strcmp(sp,".")) {
                char *sp2;

                sp2=findparam(nextpp->src,&nn->p);
                if (!strcmp(sp2,".")) sp2=findparam(nextpp->src,&nn->carryp);
                strcpy(sp,sp2);

#ifdef DEBUG
                printf("   Yes.\n");
#endif

              }
            }
          }
          writenote(pn);
        }
        if ((!(*nextlist)) && (!nn->tie)) writenote(nn);
        if (nn!=pn) {
          if (!pn->written) scoterror(Str(X_330,"Lost previous note: not written"));

#ifdef DEBUG
          if (pn->next==nn) printf("* pn->next==nn\n");
#endif

          nn->next=pn->next;

#ifdef DEBUG
          if (pn2==nn) printf("* pn2==nn\n");
#endif

          if (pn==*notetop) *notetop=nn;
          else pn2->next=nn;
          freenote(pn);
          pn=nn;

#ifdef DEBUG
          if (nn->next==nn) printf("* Circular list created\n");
#endif

        }

#ifdef DEBUG
        printf(" nn linked into note list\n");
        printf(" curtime=%lu/%lu\n",curtime->num,curtime->denom);
        printf(" nn->dur=%lu/%lu\n",nn->dur.num,nn->dur.denom);
#endif

        *ln=nn;
        ratass(lastnote,curtime);
        ratmul(&rattmp,&nn->lastdur,grpmul);
        ratadd(curtime,curtime,&rattmp);

#ifdef DEBUG
        printf(" curtime=%lu/%lu\n",curtime->num,curtime->denom);
        printf(" Done with note\n");
#endif

      }
    }
    scotungetc();
}


static                          /* puts parameter in plist */
void addparam(
    int n,                      /* number of param to change */
    char *s,                    /* parameter */
    Strlist **ptop)             /* top of list */
{
    char *ps;

    ps = findparam(n,ptop);
    if (strcmp(s,".")) strcpy(ps,s);
}


static                          /* returns pointer to */
char *findparam(int n,                      /* nth parameter */
                Strlist **ptop)             /* in plist */
{
    int z;
    Strlist *p;

    if (!(*ptop)) {
      *ptop=(Strlist *) malloc(sizeof(Strlist));
      (*ptop)->next=NULL;
      strcpy((*ptop)->str,".");
    }
    p=(*ptop);
    for (z=1;z<n;z++) {
      if (!p->next) {
        p->next=(Strlist *) malloc(sizeof(Strlist));
        p=p->next;
        p->next=NULL;
        strcpy(p->str,".");
      }
      else p=p->next;
    }
    return p->str;
}


static                          /* reads parameter list and */
char *readparams(Inst *n)       /* substitutes macros for local macro list */
{
    char *s;
    int z;

    s=(char *) malloc(300);
    z=0;
    for (;;) {
      if ((s[z]=(char) getccom())==(char) ']') break;
      z++;
    }
    s[z+1]=(char) 0;
    while (applymacs(&s,n));
    return s;
}


static                          /* substitutes 1 or 0 macros in s */
int applymacs(char **s, Inst *n) /* returns TRUE if substituted */
{
    char *news,*mv;
    int sz,nz;

#ifdef DEBUG
    printf(" Applying macros to %s\n",*s);
#endif

    news=(char *) malloc(300);
    nz=(-1);
    for (sz=0;(*s)[sz];sz++) {
      if (sz>=300) {
        scoterror(Str(X_342,"Macro expansion too long -- circular macros?"));
        return FALSE;
      }
      news[sz]=(*s)[sz];
      if (isalpha((int) (*s)[sz])) {
        if (nz==(-1)) nz=sz;
      }
      else if (nz!=(-1)) {
        news[sz]=(char) 0;
        mv=macval(&news[nz],n);
        if (mv) {
          strcpy(&news[nz],mv);
          strcat(news,&(*s)[sz]);
          free(*s);
          *s=news;
          return TRUE;
        }
        nz=(-1);
      }
    }
    free(news);
    return FALSE;
}


static                          /* returns value of macro */
char *macval(char *s, Inst *n)
{
    Macro *p;

    for (p=n->lmac;p;p=p->next) if (!strcmp(s,p->name)) return p->text;
    for (p=gmac;p;p=p->next) if (!strcmp(s,p->name)) return p->text;
    return NULL;
}


static
void initnote(Note *pn)
{
    pn->next=NULL;
    pn->p=pn->carryp=NULL;
    pn->start.num=0L; pn->start.denom=1L;
    pn->dur.num=1L; pn->dur.denom=1L;
    pn->lastdur.num=1L; pn->lastdur.denom=1L;
    pn->octave=8;
    pn->pitchclass=0;
    pn->slur=0;
    pn->tie=FALSE;
    pn->written=TRUE;
}


static                          /* reads score{} section */
void readscore(Inst *insttop)
{
    char s[128];
    Inst *p;
    Rat grpmul,timesig,curtime,lastbar,lastnote,rattmp;
    Tempo *tempotop,*tempop;
    Note *notetop,*pn,*qn,*ln;
    Nextp *nextlist,*nextpp;
    int
      accidentals,
      octaves,
      vertical,
      key[PITCHCLASSES],barkey[PITCHCLASSES],
      transpose,
      z;
    double maxtime,fcurtime;

#ifdef DEBUG
    printf("Reading score section\n");
#endif

    maxtime=0.0;
    if (expectchar('{')) scotferror(Str(X_478,"Syntax error: no {"));
    tempotop=(Tempo *) malloc(sizeof(Tempo));
    tempotop->time.num=0; tempotop->time.denom=1;
    tempotop->val=60;
    tempotop->next=NULL;
    for (;;) {
      tempop=tempotop;
      efindword(s);
      if (s[0]=='}') break;
      if (s[0]!='$') scotferror(Str(X_366,"No instrument specified"));
      p=insttop;
      while ((p!=NULL) && (strcmp(&s[1],p->name))) p=p->next;
      if (p==NULL) scotferror(Str(X_311,"Instrument not defined"));
      notetop=ln=NULL;
      grpmul.num=1; grpmul.denom=1;
      timesig.num=0; timesig.denom=1;
      curtime.num=0; curtime.denom=1;
      lastbar.num=0; lastbar.denom=1;
      lastnote.num=0; lastnote.denom=1;
      accidentals=octaves=vertical=TRUE;
      for (z=0;z<PITCHCLASSES;z++) key[z]=barkey[z]=0;
      transpose=0;
      nextlist=NULL;
      readinstsec(p,&nextlist,&grpmul,&timesig,&curtime,
                  &lastbar,&lastnote,&notetop,&ln,&tempop,&accidentals,
                  &octaves,&vertical,key,barkey,&transpose,"}$");
      for (pn=notetop;pn;pn=pn->next) {
        if (!pn->written) {
          char *ps,*ps2;

          for (nextpp=nextlist;nextpp;nextpp=nextpp->next) {
            ps=findparam(nextpp->dst,&pn->carryp);
            if (!strcmp(ps,NEXTP)) {
              ps2=findparam(nextpp->src,&pn->p);
              if (!strcmp(ps2,".")) ps2=findparam(nextpp->src,&pn->carryp);
              strcpy(ps,ps2);
            }
          }
          writenote(pn);
        }
        if (pn->tie) scoterror(Str(X_1355,"unresolved tie"));
        if (pn->slur & 1) scoterror(Str(X_1354,"unresolved slur"));
        ratadd(&rattmp,&pn->start,&pn->dur);
        if (ratcmp(&rattmp,&curtime)>0) ratass(&curtime,&rattmp);

#ifdef DEBUG
        if (pn==pn->next) scotferror(Str(X_219,"Circular note list\n"));
#endif

      }
      while (nextlist) {
        nextpp=nextlist;
        nextlist=nextlist->next;
        free((char *) nextpp);
      }
      pn=notetop;
      while (pn) {
        qn=pn;
        pn=pn->next;
        freenote(qn);
      }
      fcurtime=ratval(&curtime);
      if (fcurtime>maxtime) maxtime=fcurtime;
    }
    tempop=tempotop;
    putc('t',outfile);
    for (;;) {
      tempotop=tempop;
      tempop=tempop->next;
      fprintf(outfile,"%g %d",ratval(&tempotop->time),tempotop->val);
      free((char *)tempotop);
      if (!tempop) break;
      putc(' ',outfile);
    }
    fprintf(outfile,"\nf0 %g\ns\n",maxtime);
}


static                          /* functions{} section */
void readfunctions(void)
{
    int c;

#ifdef DEBUG
    printf("Reading function section\n");
#endif

    if (expectchar('{')) scotferror(Str(X_478,"Syntax error: no {"));
    for (;;) {
      if ((c=getccom())==EOF) scotferror(Str(X_506,"Unexpected end of file"));
      if (c=='}') {
        putc('\n',outfile);
        return;
      }
      putc(c,outfile);
    }
}


static                          /* orchestra{} section */
void readorch(Inst **insttopp)
{
    char s[128];
    Inst *p,*q = NULL;

#ifdef DEBUG
    printf("Reading orchestra section\n");
#endif

    gmac=NULL;
    if (expectchar('{')) scotferror(Str(X_478,"Syntax error: no {"));
    p=(*insttopp)=(Inst *) malloc(sizeof(Inst));
    p->lmac=NULL;
    for (;;) {
      efindword(s);
      if (s[0]==(char) '}') break;
      if (s[0]==(char) '[') {
        if (p==(*insttopp)) readmacros(&gmac);
        else readmacros(&q->lmac);
        continue;
      }
      p->name=(char *) malloc(strlen(s)+1);
      strcpy(p->name,s);

#ifdef DEBUG
      printf("Instrument name: %s ",p->name);
#endif

      if (expectchar('=')) scoterror(Str(X_476,"Syntax error: no ="));
      if (findint((int *)&p->number))
        scoterror(Str(X_477,"Syntax error: no number"));

#ifdef DEBUG
      printf(" number: %d\n",p->number);
#endif

      p->next = (Inst *) malloc(sizeof(Inst));
      q       = p;
      p       = p->next;
      p->lmac = NULL;
    }
    if (p==*insttopp) scotferror(Str(X_367,"No instruments declared"));
    free((char *)p);
    q->next=NULL;
}


static                          /* reads macro list from score section */
void readmacros(Macro **mtop)
{
    Macro *p,*q;

#ifdef DEBUG
    printf("Reading macro definitions\n");
#endif

    p=(*mtop)=(Macro *) malloc(sizeof(Macro));
    efindword(p->name);
    for (;;) {
      q=p;
      if (p->name[0]==(char) ']') break;
      if (expectchar('=')) scoterror(Str(X_260,"Expected ="));
      efindword(p->text);

#ifdef DEBUG
      printf(" %s = %s\n",p->name,p->text);
#endif

      p=p->next=(Macro *) malloc(sizeof(Macro));
      efindword(p->name);
    }
    q->next=NULL;
    free((char *)p);
}


static                          /* returns TRUE if ch is NOT */
int expectchar(int ch)          /* the next non-whitespace char */
{
    int c;

    do {
      if ((c=getccom())==EOF) return TRUE;
    } while (strchr(" \t\r\n",c));
    return !(c==ch);
}


/* all these find* functions return their found values in
   their passed-by-pointer operands. They only return TRUE
   upon failure. */


static
int findchar(int *ip)
{
    int c;

    do {
      if ((c=getccom())==EOF) scotferror(Str(X_506,"Unexpected end of file"));
    } while (strchr(" \t\r\n",c));
    *ip=c;
    return FALSE;
}


static
int findint(int *ip)
{
    int c,t;

    t=TRUE;
    *ip=0;
    do {
      if ((c=getccom())==EOF) scotferror(Str(X_506,"Unexpected end of file"));
    } while (strchr(" \t\r\n",c));
    while (isdigit(c)) {
      t=FALSE;
      *ip*=10;
      *ip+=(c-'0');
      if ((c=getccom())==EOF) scotferror(Str(X_506,"Unexpected end of file"));
    }
    scotungetc();
    return t;
}


static
int findonoff(int *ip)
{
    char s[32];

    efindword(s);
    if (!strcmp(s,"on")) {
      *ip=TRUE;
      return FALSE;
    }
    if (!strcmp(s,"off")) {
      *ip=FALSE;
      return FALSE;
    }
    return TRUE;
}


static
void efindword(char *s)
{
    if (findword(s)) scotferror(Str(X_506,"Unexpected end of file"));
}


static
int findword(char *s)
{
    int c,n;

    n=0;
    do {
      if ((c=getccom())==EOF) return TRUE;
    } while (strchr(" \t\r\n",c));
    if (c=='\"') {
      while ((c=scotgetc())!='\"') {
        if (c==EOF) return TRUE;
        s[n++]=(char) c;
      }
      s[n]=(char) 0;
      return FALSE;
    }
    if (strchr("{}[]",c)) {
      s[0]=(char) c;
      s[1]=(char) 0;
      return FALSE;
    }
    do {
      s[n++]=(char) c;
      if ((c=getccom())==EOF) return TRUE;
    } while (!strchr(" \t\r\n{}[]=\"",c));
    s[n]=(char) 0;
    scotungetc();
    return FALSE;
}


static                          /* gets a char from file, but */
int getccom(void)                       /* ignores comments. */
{
    int c;

    c=scotgetc();
    if (c!=';') return c;
    while (scotgetc()!='\n');
    return '\n';
}


static
int letterval(int c)
{
    switch (c) {
    case 'a':
      return 5;
    case 'b':
      return 6;
    case 'c':
      return 0;
    case 'd':
      return 1;
    case 'e':
      return 2;
    case 'f':
      return 3;
    case 'g':
      return 4;
    default:
      scoterror(Str(X_316,"Invalid pitch class"));
      return 0;
    }
}


static                          /* returns octave.pitchclass */
double pitchval(int oct, int pit, int acc, int transpose)
{
#ifdef DEBUG
    printf("  Computing pitchval(%d,%d,%d,%d)\n",oct,pit,acc,transpose);
#endif

    pit=naturals[pit]+acc+transpose;

#ifdef DEBUG
    printf("  pit=%d\n",pit);
#endif

    while (pit<0) {
      pit+=NOTESPEROCT;
      oct--;

#ifdef DEBUG
      printf("  oct=%d pit=%d\n",oct,pit);
#endif

    }
    while (pit>=NOTESPEROCT) {
      pit-=NOTESPEROCT;
      oct++;

#ifdef DEBUG
      printf("  oct=%d pit=%d\n",oct,pit);
#endif

    }

#ifdef DEBUG
    printf("  pitchval: %d.%02d\n",oct,pit);
#endif

    return (double) oct+(double) pit*0.01;
}


static                          /* just writes pfields from n->p, */
void writenote(Note *n)         /* or n->carryp if others are blank "." */
{
    Strlist *ps,*cps;

#ifdef DEBUG
    printf(" writing: i");
#endif

    n->written=TRUE;
    putc('i',outfile);
    ps=n->p;
    cps=n->carryp;
    for (;;) {
      if (ps && strcmp(ps->str,".")) {
        fputs(ps->str,outfile);

#ifdef DEBUG
        printf(" %s",ps->str);
#endif

      }
      else if (cps && strcmp(cps->str,".")) {
        fputs(cps->str,outfile);

#ifdef DEBUG
        printf(" %s(c)",cps->str);
#endif

      }
      else /* if (ps || cps) */ {
        fputs(" 0",outfile);

#ifdef DEBUG
        printf(" 0(.)");
#endif

      }
      if (ps) ps=ps->next;
      if (cps) cps=cps->next;
      if (!(ps || cps)) break;
      putc(' ',outfile);
    }
    putc('\n',outfile);

#ifdef DEBUG
    printf("\n");
#endif

}


static                          /* deallocates a note and */
void freenote(Note *n)          /* its pfield lists */
{
    freeps(n->p);
    freeps(n->carryp);
    free((char *)n);
}


static                          /* deallocates a pfield list */
void freeps(Strlist *pp)
{
    Strlist *pq;

    while (pp) {
      pq=pp;
      pp=pp->next;
      free((char *)pq);
    }
}


static                          /* copies a pfield list */
void strlistcopy(Strlist **dest,Strlist **source)
{
    Strlist *sp,*dp;

    if ((*source)==NULL) {
      *dest=NULL;
      return;
    }
    sp=(*source);
    dp=(*dest)=(Strlist *) malloc(sizeof(Strlist));
    for (;;) {
      strcpy(dp->str,sp->str);
      sp=sp->next;
      if (sp==NULL) break;
      dp=dp->next=(Strlist *) malloc(sizeof(Strlist));
    }
    dp->next=NULL;
}


/* rational number functions */

static
double ratval(Rat *r)           /* evaluate r */
{
    if (!r->denom) {
      scoterror(Str(X_246,"Division by zero"));
      return (double) 1.0;
    }
    return (double) r->num/(double) r->denom;
}


static
void ratreduce(Rat *r)          /* reduce r */
{
    unsigned long a,b,x;

    if (!r->num) {
      r->denom = 1;
      return;
    }
    if (!r->denom) {
      scoterror(Str(X_246,"Division by zero"));
      return;
    }
    if (r->num > r->denom) {
      a        = r->num;
      b        = r->denom;
    }
    else {
      a        = r->denom;
      b        = r->num;
    }
    while ((x  = a%b)) {
      a        = b;
      b        = x;
    }
    r->num    /= b;
    r->denom  /= b;
}


static
void ratadd(Rat *d,Rat *a1,Rat *a2)             /* d=a1+a2; OK if d=a1=a2 */
{
    unsigned long lcd;

#ifdef DEBUG
    printf("  ratadd (%lu/%lu)+(%lu/%lu)\n",a1->num,a1->denom,a2->num,a2->denom);
#endif

    lcd=(a1->denom)*(a2->denom);
    d->num=(a1->num)*(a2->denom)+(a2->num)*(a1->denom);
    d->denom=lcd;

#ifdef DEBUG
    printf("  unreduced result: %lu/%lu\n",d->num,d->denom);
#endif

    ratreduce(d);

#ifdef DEBUG
    printf("  reduced result: %lu/%lu\n",d->num,d->denom);
#endif

}


static
void ratmul(Rat *d,Rat *a1,Rat *a2)             /* d=a1*a2; OK if d=a1=a2 */
{
    d->num=a1->num*a2->num;
    d->denom=a1->denom*a2->denom;
    ratreduce(d);
}


static
void ratdiv(Rat *d,Rat *a1,Rat *a2)             /* d=a1/a2; OK if d=a1=a2 */
{
    if (!a2->num) {
      scoterror(Str(X_246,"Division by zero"));
      ratass(d,a1);
      return;
    }
    d->num=a1->num*a2->denom;
    d->denom=a1->denom*a2->num;
    ratreduce(d);
}


static
int ratcmp(Rat *a,Rat *b)
{
    unsigned long m,n;

    m=a->num*b->denom;
    n=b->num*a->denom;
    if (m<n) return -1;
    if (m==n) return 0;
    return 1;
}


static
void ratass(Rat *a,Rat *b)              /* sorry about the name... a=b; */
{
    a->num=b->num;
    a->denom=b->denom;
}



/* i/o and error routines */


static                          /* call once to start reading infile */
void initf(FILE *inf,FILE *outf,char *fil)
{
    infile=inf;
    outfile=outf;
    infilename=fil;
    fgets(curline,MAXLINE,infile);

#ifdef DEBUG
    printf("1: %s",curline);
#endif

    inx=0; iny=1;
    errcount=0;
}


static                          /* use like getc(infile) */
int scotgetc(void)
{
    if (inx<0) {
      inx=0;
      return '\n';
    }
    if (curline[inx]==(char)0) {
      if (!fgets(curline,MAXLINE,infile)) return EOF;
      inx=0;
      iny++;

#ifdef DEBUG
      printf("%d: %s",iny,curline);
#endif

    }
    inx++;
    return (int) curline[inx-1];
}


static
void scotungetc(void)
{
    inx--;
}


static
void scoterror(char *s)
{
    int z;

    fprintf(stderr,"%s(%d) %s\n",infilename,iny,s);
    fputs(curline,stderr);
    for (z=1;z<inx;z++) putc(' ',stderr);
    fprintf(stderr,"^\n");
    errcount++;
}


static                          /* fatal error */
void scotferror(char *s)
{
    scoterror(s);
    fprintf(stderr,Str(X_1180,"scot processing terminated\n"));
    reporterrcount();
    exit(1);
}


static
void reporterrcount(void)
{
    fprintf(stderr,Str(X_1181,"scot: %d errors.\n"),errcount);
}
