Chapter 8
Groups and Group-Based Inline Operations

   8.1 Data Transfer
      Main Pass
      Memory
      Initialization
   8.2 Submissions and Retievals
      “Forward” to End of Group
      “Backward” to Begin of Groups
   8.3 Preprocessing Pass
      Specials
      Fonts and Default
      Skipped Content
   8.4 Push/Pop Locations
   8.5 Backward Token / Group Submissions
      Scan to Record Recieving Points
      Memory for Linked Lists
      Scan Backward Submissions
      Use Backward Submissions
   8.6 Actions on Forward Groups Accessed Through Pathes
      Motivation
      Record Request
      Process Path Lists At Entry To Group
      Process Individual Records at Entry To Group
      Path Group At Exit From Group
      Process Individual Records at Exit from Group

For instance, \over. Such operations require delivery of information backward and forward to the group boundaries. For backward delivery, a preprocessing phase is introduced.

8.1 Data Transfer

Upon encountering a \special{t4ht~...} we arrive here. An empty content is an on/off flag. When it is turned on, we make a preprocessing pass to collect information.

<..grouped-base delivery content..>
 if( special_n ){
   <.main pass for group special.>
 } else if( (group_dvi = !group_dvi) == TRUE ){
                long  curr_pos;
                int   ch, sv_stack_n;
                <.subp vars.>
   <.init subp vars.>  stack_id = 0;
   curr_pos = ftell(dvi_file);  sv_stack_n = stack_n;
   <.send info backward.>
   <.exit early pass.>
   (IGNORED) fseek(dvi_file, curr_pos, <.abs file addr.>);
   group_dvi = TRUE;  stack_n = sv_stack_n;    stack_id = 0;
 } else { <.exit group special.> }
 -_-_-

<..vars..>+
 static BOOL group_dvi = FALSE;
 -_-_-

<..main’s vars..>+
 int stack_id=0;
 -_-_-

Main Pass

<..main pass for group special..>
               U_CHAR in_ch;
 if( (in_ch = get_char()) == ’>’ ) {
   <.store forward submissions.>
 } else if( in_ch == ’!’ ) {
   <.store path-based forward submissions.>
 } else {
   if( !group_dvi ){ warn_i(42); }
   (IGNORED) fseek(dvi_file, (long) --special_n,
                              <.relative file addr.>);
   special_n = 0;
 }
 -_-_-

[example]

Memory

<..vars..>+
 static int stack_n = 0;
 static struct stack_entry* stack;
 -_-_-

<..types..>+
 struct stack_entry{
   long  int x_val, y_val;
   INTEGER dx_1, dx_2, dy_1, dy_2;
   BOOL text_on;
   BOOL <.BOOl stack accented.>;
   <.struct stack_entry.>
 };
 -_-_-

<..stack = m-alloc.....>
 stack = m_alloc(struct stack_entry,<.size of stack for nested boxes.>);
 <.initialize grouping stack.>
 -_-_-

<..size of stack for nested boxes..>
 ((int) stack_len + 2)
 -_-_-

The ‘\special{t4ht^>*...*...}’ asks for delimiters on the next group or token, whichever comes first. That information is stored in the stack even for characters, which implies an implicit stack greater by one than that requested by the dvi code. The extra 2 in size reults from the pointer being on the next empty entry?

<..struct stack_entry..>+
 int stack_id;
 struct group_info * begin;
 struct stack_end_entry * end;
 -_-_-

<..types..>+
 struct group_info{
   int stack_id;
   U_CHAR *info;
   struct group_info* next;
 };
 -_-_-

Initialization

<..initialize grouping stack..>
 {                   int i;
   for( i=<.size of stack for nested boxes.>-1; i>=0; i--){
     stack[i].begin = (struct group_info *) 0;
     stack[i].end   = (struct stack_end_entry *) 0;
     stack[i].stack_id = -1;
     <.initialized entries for grouping stack.>
 } }
 -_-_-

8.2 Submissions and Retievals

“Forward” to End of Group

<..store forward submissions..>
 if( special_n == 1 ){
    special_n--;
    switch( get_char() ){
      case ’[’: { ignore_end_group++;  break; }
      case ’]’: { ignore_end_group--;  break; }
       default: { <.unused special.> }
    }
 } else {
              struct stack_end_entry *p;
              U_CHAR                   *q;
              int                     j;
    j = get_char() - ’0’ + stack_n - 1;
    if( --special_n ){
      if (j >= stack_len ) { j = stack_len - 1; }
      p = m_alloc(struct stack_end_entry,1);
      p->next =  stack[ j ].end;
      stack[ j ].end = p;
      q = p->send = m_alloc(char,special_n+1);
      while( --special_n ) *q++ = get_char();
      *q = ’\0’;
 }  }
 -_-_-

We create a linked list for forward submissions.

<..types..>+
 struct stack_end_entry{
   struct stack_end_entry *next;
   U_CHAR *send;
 };
 -_-_-

<..retrieve forward submissions..>
           struct stack_end_entry *q, *p, *t;
 q = stack[ stack_n-1 ].end;
 p = stack[ stack_n-1 ].end = (struct stack_end_entry *) 0;
 while( q ){
   t = q->next;  q->next  = p;  p = q;  q = t;
 }
 while( p ){
   if( ! ignore_end_group ){ print_f( p->send ); }
   free((void *)  p->send );
   q = p;  p = p->next;    free((void *)  q );
 }
 -_-_-

<..get forward deliveries..>
 while( stack[stack_n-1].end ){
   <.retrieve forward submissions.>
 }
 -_-_-

<..vars..>+
 static int ignore_end_group;
 -_-_-

<..exit group special..>
 {              int stack_n;
   for( stack_n=<.size of stack for nested boxes.>;
        stack_n>0; stack_n--){
     group_dvi = TRUE;    <.get forward deliveries.>
     group_dvi =FALSE;
     <.check forward deliveries.>
 } }
 -_-_-

<..check forward deliveries..>
 while( stack[stack_n-1].begin ){
                                struct group_info *p;
    warn_i_str(44, stack[stack_n-1].begin->info);
    p =  stack[stack_n-1].begin;
    stack[stack_n-1].begin = p->next;
    free((void *)  p );
 }
 stack[stack_n-1].stack_id = -1;
 -_-_-

“Backward” to Begin of Groups

The following is needed for pops that appear before the leading push. Such pops can emerge from preceding code.

<..send info back to group open..>
           struct group_info *p;
           U_CHAR *q;
           int j;
 j = ch - ’0’ + stack_n - 1;
 if (j >= stack_len ) { j = stack_len - 1; }
 p = m_alloc(struct group_info,1);
 p->next = stack[ j ].begin; stack[ j ].begin = p;
 p->stack_id = stack[ j ].stack_id;
 q = p->info = m_alloc(char,i+1);
 while( --i ) *q++ = get_char();
 *q = ’\0’;
 -_-_-

“Backward” submissions to levels higher than 0 are forward transmission and they can be moved to the main pass. That will buy a little saving in memory, and possibly less fragmentation there due to dynamic allocation of mem.

<..push/pop on backward submissions..>
 case <.sv loc op.>: {
    <.stack-id into linked list.>
    stack[stack_n].stack_id = stack_id++;
    <.push-id + 1.>    stack_n++;
    if( stack_n > <.size of stack for nested boxes.> ){ warn_i(40); }
    break;
 }
 case <.retrieve loc op.>: {
    stack_n--;  <.pop-id + 1.>
    stack[stack_n].stack_id = -1;
    break;
 }
 -_-_-

The following changes the previously unknown stack-id, represented by -1, of a future deeper PUSH with the stack-id of that PUSH. That PUSH is just have been reached. We also want to reverse the order within that sub-list.

<..stack-id into linked list..>
 {     struct group_info *p, *last;
   if( (last = p = stack[ stack_n ].begin) != (struct group_info *)0 )
     if( p->stack_id == -1 ){
       <.modify id of sub sequence.>
       <.reverse stack sub sequence.>
     }
 }
 -_-_-

<..modify id of sub sequence..>
 while( p ){
   if( p->stack_id != -1 ){ break; }
   p->stack_id = stack_id;
   last = p;
   p = p->next;
 }
 -_-_-

<..reverse stack sub sequence..>
 while ( stack[ stack_n ].begin != last ){
   p = (stack[ stack_n ].begin) -> next;
   (stack[ stack_n ].begin) -> next = last->next;
   last->next = stack[ stack_n ].begin;
   stack[ stack_n ].begin = p;
 }
 -_-_-

<..get backward deliveries..>
 {                        struct group_info *p;
   if( group_dvi &&
       ( (p = stack[stack_n].begin )  != (struct group_info *)0)
     ){
      while( p ){
        if( p->stack_id != stack_id ) break;
        print_f(p->info);
        stack[stack_n].begin = p->next;
        free((void *)  p );
        p = stack[stack_n].begin;
      }
   }
   stack_id++;
 }
 -_-_-

The following reverses the list at the end of the preprocessing pass.

<..exit early pass..>
 {                  struct group_info  *first, *second, *temp;
                    int i;
 for(i = stack_len; i >= 0; i--){
   first = stack[i].begin;
   if( first ) {
      second = first->next;
      while( second  ){
        temp =  second->next;
        second->next = first;
        first = second;
        second = temp;
      }
      (stack[i].begin)->next = (struct group_info  *) 0;
      stack[i].begin = first;
 } } }
 -_-_-

8.3 Preprocessing Pass

Used for collecting information to be sent backward to the entry point of the group. The information is stored in linked lists of type ‘struct group_info’, accessed through ‘stack[ level-of-nesting ].begin’.

Each PUSH is offered an id. Upon reaching a PUSH, its id is stored in ‘stack[ level-of-nesting ].stack_id’. Upon reaching a ‘\special{t4ht~<...message...}’, the message is inserted into the linked list, together with its PUSH id. The messages are retrieved in the main pass, as the PUSHES are traversed for the second time.

<..send info backward..>
 while( group_dvi ){
   <.extract backward submissions.>
 }
 -_-_-

Specials

Only ‘~’ tex4ht specials are significant here. The ones of the form ‘\special{t4ht~}’ act as end-points for the preprocessing, and ‘\special{t4ht~<...}’ act as backward info delivers.

<..hooks for backward submissions..>
 case <.special 1.>:  case <.special 2.>:
 case <.special 3.>:  case <.special 4.>: {  long int i;
   if( tex4ht_special( &ch, &i ) ){
      if( ch == ’~’ ){
         <.handle requests for backward submissions.>
      } else {
        (IGNORED) fseek(dvi_file, (long) i, <.relative file addr.>);
      }
   }else{ <.ignore non-t4ht special.>  }
   break;
 }
 -_-_-

<..ignore non-t4ht special..>
    U_CHAR *ch;
 ch = special_hd + 4;
 while( *ch ){   ch++; }
 (IGNORED) fseek(dvi_file, (long) i, <.relative file addr.>);
 -_-_-

<..handle requests for backward submissions..>
 if( i==0 ){
   group_dvi = FALSE ;
 }else{
   switch( get_char() ){
      case ’<’: {
         if( i-- ){         U_CHAR ch;
            if( (ch = get_char()) == ’*’ )
              { <.send back over token / group.> }
            else if( (ch == ’[’) && (i==1) ){
              i--;  <.hide back token / group.>
            }
            else if( (ch == ’]’) && (i==1) ){
              i--;  <.end hide back token / group.>
            }
            else if( (ch == ’-’) && (i==1) ){
              i--;  <.latex back token / group.>
            }
            else if( (ch == ’+’) && (i==1) ){
              i--;  <.end latex back token / group.>
            }
            else if( (ch == ’(’) && (i==1) ){
              i--;  <.back token / group.>
            }
            else if( (ch == ’)’) && (i==1) ){
              i--;  <.end back token / group.>
            }
            else { <.send info back to group open.> }
         }
         break; }
      default: { (IGNORED) fseek(dvi_file, (long) --i,
                                <.relative file addr.>);  break; }
 } }
 -_-_-

When sv_stack_n==stack_n the sending back NEED NOT be in the level of the opening \special{t4ht~}. This is so because it can happen that we have, for instance, ‘\special ~<POP><PUSH>

Fonts and Default

Fonts Definition:

<..ignore font def on preview pass..>
 case <.def 4 byte font.>: (void) get_char();
 case <.def 3 byte font.>: (void) get_char();
 case <.def 2 byte font.>: (void) get_char();
 case <.def 1 byte font.>: {    int i;
   for( i=14; i; i-- ){  ch = get_char(); }
   i = ch +  get_char();
   (IGNORED) fseek(dvi_file, (long) i, <.relative file addr.>);
   break;  }
 -_-_-

Fonts Activation:

<..fonts and default ignored on preview pass..>
 case  <.font 1-byte.>:
 case <.font 2-bytes.>:
 case <.font 3-bytes.>:
 case     <.font int.>: {
                               INTEGER n;
   n = ch - <.font 1-byte.> + 1;
   cr_fnt = (int)  ((n==4)? get_int(4) : get_unt((int) n));
   cr_fnt = search_font_tbl( cr_fnt );
   break; }
 default: {
   if( (ch < <.font 0.>) || (ch > <.font 63.>) ) {
      if( ch == <.end page op.> ) { warn_i(46); }
      else { warn_i_int(45,ch); }
   } else { cr_fnt = ch - <.font 0.>;
          cr_fnt = search_font_tbl( cr_fnt );
   }
   break;
 }
 -_-_-

<..subp vars..>
 int cr_fnt;
 -_-_-

<..init subp vars..>
 cr_fnt = cur_fnt;
 -_-_-

Skipped Content

<..extract backward submissions..>
 if( (ch = get_char()) >= 128 ) {
   switch( ch ){
     <.indirect chars on early backward submissions pass.>
     <.ignore font def on preview pass.>
     <.ignore on preview pass.>
     <.push/pop on backward submissions.>
     <.hooks for backward submissions.>
     <.fonts and default ignored on preview pass.>
   }
 } else { <.ch-id + 1.> }
 -_-_-

<..indirect chars on early backward submissions pass..>
 case 128: case 129: case 130: case 131: case 133:
 case 134: case 135: case 136: {
   ch = (int) get_unt( (ch-(ch>132)) % 4 +1);
   <.ch-id + 1.>
   break;
 }
 -_-_-

<..ignore on preview pass..>
 case <.insert rule + move op.>:
 case <.insert rule + nomove op.>:{
   (IGNORED) fseek(dvi_file, 8L, <.relative file addr.>);
   break;
 }
 -_-_-

<..ignore on preview pass..>+
 case   <.start page op.>: {
   (IGNORED) fseek(dvi_file, 44L, <.relative file addr.>);  break; }
 -_-_-

<..ignore on preview pass..>+
 case <.mv hor 1-byte.>: case <.mv hor 2-byte.>:
 case <.mv hor 3-byte.>: case <.mv hor 4-byte.>: {
     (IGNORED) (get_int( ch - <.mv hor 1-byte.> + 1 ));  break; }
 case <.dx.1 store and mv hor 1-byte.>:
 case <.dx.1 store and mv hor 2-byte.>:
 case <.dx.1 store and mv hor 3-byte.>:
 case <.dx.1 store and mv hor 4-byte.>: {
     (IGNORED) (get_int( ch - <.dx.1 store and mv hor 1-byte.> + 1));
     break;  }
 case <.dx.2 store and mv hor 1-byte.>:
 case <.dx.2 store and mv hor 2-byte.>:
 case <.dx.2 store and mv hor 3-byte.>:
 case <.dx.2 store and mv hor 4-byte.>: {
     (IGNORED) (get_int( ch - <.dx.2 store and mv hor 1-byte.> + 1));
     break;  }
 case <.mv ver 1-byte.>: case <.mv ver 2-byte.>:
 case <.mv ver 3-byte.>: case <.mv ver 4-byte.>: {
     (IGNORED) (get_int( ch - <.mv ver 1-byte.> + 1));
     break; }
 case <.dy.1 store and mv ver 1-byte.>:
 case <.dy.1 store and mv ver 2-byte.>:
 case <.dy.1 store and mv ver 3-byte.>:
 case <.dy.1 store and mv ver 4-byte.>: {
     (IGNORED) (get_int( ch - <.dy.1 store and mv ver 1-byte.> + 1));
     break; }
 case <.dy.2 store and mv ver 1-byte.>:
 case <.dy.2 store and mv ver 2-byte.>:
 case <.dy.2 store and mv ver 3-byte.>:
 case <.dy.2 store and mv ver 4-byte.>: {
     (IGNORED) (get_int( ch - <.dy.2 store and mv ver 1-byte.> + 1));
     break; }
 -_-_-

<..ignore on preview pass..>+
 case <.mv hor dist dx.1.>:
 case <.mv hor dist dx.2.>:
 case <.mv ver dist dy.1.>:
 case <.mv ver dist dy.2.>:
    { break; }
 -_-_-

8.4 Push/Pop Locations

<..process dvi op ’ch’..>+
 case     <.sv loc op.>: { <.push values to stack.>   break; }
 case <.retrieve loc op.>: { <.pop values from stack.>  break; }
 -_-_-

The dvi stacking code refers to the information ‘x_val’, ‘y_val’, ‘dx_1’, ‘dx_2’, ‘dy_1’, and ‘dy_2’.

<..push values to stack..>
 <.halign at entry to group.>
 <.get backward deliveries.>
 stack[stack_n].text_on = text_on;
 push_stack();  <.get back sub/sup before group.>
 <.path group at entry to group.>
 <.trace dvi push.>
 <.add push class del.>
 -_-_-

<..header functions..>+
 static void push_stack( ARG_I(void) );
 -_-_-

<..functions..>+
 static void push_stack(MYVOID)
 {
    stack[stack_n].x_val = x_val;
    stack[stack_n].dx_1  = dx_1;
    stack[stack_n].dx_2  = dx_2;
    stack[stack_n].y_val = y_val;
    stack[stack_n].dy_1  = dy_1;
    stack[stack_n].dy_2  = dy_2;
    <.push inherit del.>
    stack_n++;
 if( stack_n > <.size of stack for nested boxes.> ){
   warn_i(40);
 }
    <.init end math accented.>
 }
 -_-_-

<..push inherit del..>
 stack[stack_n+1].sv_no_left_del = stack[stack_n+1].no_left_del;
 stack[stack_n+1].no_left_del = stack[stack_n].no_left_del;
 -_-_-

<..pop uninherit del..>
 stack[stack_n].no_left_del = stack[stack_n].sv_no_left_del;
 -_-_-

The first statement partially handles spaces after text that is inserterted to the left of the current location (e.g., items of list).

<..pop values from stack..>
 <.add pop class del.>
 <.path group at exit from group.>
 <.get forward deliveries.>
 <.trace dvi pop.>
 <.halign at exit from group.>
 pop_stack();
 if( ((x_val+0.6*word_sp) <  stack[stack_n].x_val) )  put_char(’ ’);
 text_on = stack[stack_n].text_on;
 -_-_-

<..header functions..>+
 static void pop_stack( ARG_I(void) );
 -_-_-

<..functions..>+
 static void pop_stack(MYVOID)
 {
    <.end math accented.>
    <.pop uninherit del.>
    --stack_n;
    x_val = stack[stack_n].x_val;
    dx_1  = stack[stack_n].dx_1;
    dx_2  = stack[stack_n].dx_2;
    y_val = stack[stack_n].y_val;
    dy_1  = stack[stack_n].dy_1;
    dy_2  = stack[stack_n].dy_2;
 }
 -_-_-

8.5 Backward Token / Group Submissions

TeX places subscripts and superscripts on the most recent group or token. We are looking where the most recent of these tokens starts, and store there the desired info.

Scan to Record Recieving Points

We keep a delimiter stack for the parenthesis, and include there also the opening and closing of groups. The latter one are employed to deal with parenthese that are not balanced, for instance, ‘\left( ... \right.’.

<..pop-id + 1..>
 if( !back_id_off ){
    if( !id_hide ){  ch_token = FALSE;
                     sv_id = stack[stack_n].stack_id; }
    while( del_stack != (struct del_stack_entry*) 0 ){
                                      struct del_stack_entry* p;
                                      int id;
      del_stack = (p = del_stack)->next;
      id = p->id;
      free((void *)  p );
      if( id == -1 ) break;
 }  }
 -_-_-

<..push-id + 1..>
 if( !back_id_off )
 {                    struct del_stack_entry *p;
    p = m_alloc(struct del_stack_entry,1);
    p->next = del_stack;
    p->id = p->fnt = -1;
    del_stack = p;
 }
 -_-_-

<..ch-id + 1..>
 ch_id++;
 if(!back_id_off ){
    if( !id_hide ){  ch_token = TRUE;  sv_id = ch_id; }
    switch( math_class_of( ch, cr_fnt ) ){
      case <.math open.>: { del_stack = push_del( (char) ch, cr_fnt);
                            break; }
      case <.math close.>: {
         del_stack = pop_del( (char) ch, id_hide, cr_fnt);   break; }
      default:{ ; }
 }  }
 -_-_-

<..math close..>
 5
 -_-_-

<..math open..>
 4
 -_-_-

Assumption made: closing del is in the same font as the opening one.

<..header functions..>+
 static struct del_stack_entry* push_del( ARG_II(char, int) );
 -_-_-

<..functions..>+
 
 static  struct del_stack_entry* push_del(ch, cr_fnt)     U_CHAR ch;
                                                  int cr_fnt
 ;{                    struct del_stack_entry *p;
     p = m_alloc(struct del_stack_entry,1);
     p->next = del_stack;
     p->ch = ch;
     p->fnt = cr_fnt;
     p->id = ch_id;
     return p;
 }
 -_-_-

<..header functions..>+
 static struct del_stack_entry* pop_del( ARG_III(char,int,int) );
 -_-_-

<..functions..>+
 
 static  struct del_stack_entry* pop_del(ch, id_hide, cr_fnt)
                                                   U_CHAR ch;
                                                   int  id_hide;
                                                   int cr_fnt
 ;{
    if( del_stack != (struct del_stack_entry*) 0 ){
       if( (cr_fnt ==  del_stack->fnt) &&
           (  *(font_tbl[cr_fnt].math + (ch - font_tbl[cr_fnt].char_f))
              == del_stack->ch) ){
                                              struct del_stack_entry  * p;
          if( !id_hide && !id_latex ){  sv_id = del_stack->id; }
          del_stack = (p = del_stack)->next;  free((void *)  p );
    }  }
    return del_stack;
 }
 -_-_-

What the id_latex does here?

<..vars..>+
 static struct del_stack_entry  *del_stack;
 -_-_-

<..main’s init..>+
 del_stack = (struct del_stack_entry  *) 0;
 -_-_-

<..types..>+
 struct del_stack_entry{
   struct del_stack_entry *next;
   U_CHAR ch;
   int  fnt, id;
 };
 -_-_-

<..vars..>+
 static int ch_id, sv_id, id_latex, back_id_off;
 -_-_-

<..main’s init..>+
 back_id_off = 1;  id_latex = 0;
 -_-_-

<..init ch-id..>
 ch_id = 0;
 -_-_-

<..subp vars..>+
 BOOL  ch_token;
 int  id_hide;
 -_-_-

<..init subp vars..>+
 sv_id = 0;    <.init ch-id.>
 id_hide = 0;    ch_token = TRUE;
 while( del_stack != (struct del_stack_entry*) 0 ){
                                   struct del_stack_entry* p;
   del_stack = (p = del_stack)->next;
   free((void *)  p );
 }
 -_-_-

<..hide back token / group..>
 id_hide++;
 -_-_-

<..end hide back token / group..>
 id_hide--;
 -_-_-

<..latex back token / group..>
 id_latex++;
 -_-_-

<..end latex back token / group..>
 id_latex--;
 -_-_-

<..back token / group..>
 back_id_off++;
 -_-_-

<..end back token / group..>
 back_id_off--;
 -_-_-

Memory for Linked Lists

<..types..>+
 struct send_back_entry{
   struct send_back_entry *next;
   U_CHAR *send;
   int  id;
 };
 -_-_-

<..vars..>+
 static struct send_back_entry *back_token, *back_group;
 -_-_-

<..main’s init..>+
 back_token = back_group = m_alloc(struct send_back_entry,1);
 back_token->id = -1;
 -_-_-

Scan Backward Submissions

<..send back over token / group..>
               struct send_back_entry *p, *q, *t=0;
 if( back_id_off ){
    while( i-- ){ (IGNORED) get_char();  }
 } else {
    p =  m_alloc(struct send_back_entry,1);
    p->send = get_str( (int)( i - 1 ));
    if( ch_token ){
      <.send back to char.>
    } else {
      p->id = (sv_id<0? 0 : sv_id) +  push_id;
      if( back_group->id < p->id )
         {  p->next = back_group;   back_group = p;  }
      else
         {  q = back_group;
            while( q->id >= p->id ) { t = q;  q = q->next;  }
            p->next = t->next;   t->next = p;
         }
    }
 }
 -_-_-

Keep [consistency] between back on group and back on token

<..send back to char..>
 p->id = sv_id;
 if( sv_id >  back_token->id ){
    p->next = back_token;   back_token = p;
 } else {
    q = back_token;
    while( sv_id <= q->id ){ t = q;  q = q->next;  }
    p->next = t->next;   t->next = p;
 }
 -_-_-

The following reverses the linked lists for ids and groups, so that the early entries will get at the head instead of the tail.

<..exit early pass..>+
 back_group = rev_list( back_group );
 back_token = rev_list( back_token );
 back_token = back_insert ( back_token, 0);
 <.init ch-id.>
 -_-_-

<..header functions..>+
 static
 struct send_back_entry * rev_list( ARG_I(struct send_back_entry *) );
 -_-_-

<..functions..>+
 
 static struct send_back_entry *  rev_list(back_group)
                              struct send_back_entry *back_group
 ;{                           struct send_back_entry *p, *q, *t;
     if( back_group->id == -1 ){ return back_group; }
     p = back_group;    q = p->next;
     while( p->id != -1 ){
        t = q->next;  q->next = p;  p = q;  q = t;
     }
     back_group->next = p;
     return  p->next;
 }
 -_-_-

Use Backward Submissions

<..get back sub/sup before group..>
 if( group_dvi ) {
    back_group = back_insert ( back_group, push_id);
 }
 -_-_-

<..get back sub/sup before ch..>
 if( group_dvi ){
   if( ( ch < 132 ) ||
       ( (ch > 127) && (ch < 137) && (ch != <.insert rule + move op.> ) )
     ){
        ch_id++;
        back_token = back_insert ( back_token, ch_id);
 } }
 -_-_-

<..header functions..>+
 static struct send_back_entry *
    back_insert( ARG_II(struct send_back_entry *, int) );
 -_-_-

<..functions..>+
 
 static struct send_back_entry *  back_insert(back, id)
                              struct send_back_entry *back;
                              int    id
 ;{
   while( back->id == id ){
                          struct send_back_entry *p;
     print_f( back->send );
     back = (p = back)->next;
     free((void *)  p->send );
     free((void *)  p );
   }
   return back;
 }
 -_-_-

8.6 Actions on Forward Groups Accessed Through Pathes

Motivation

The current instructions are motivated by the ‘\sqrt’ structure

PUSH 
   PUSH 
      ......sqrt et al symbols, possibly with PUSH-POP...... 
   POP 
   PUSH 
      W3:       655361 
      DOWN3:    -1020474 
      PUT_RULE: height: 26213 
                length: 1091179 
      DOWN3:    1020474 
      PUSH 
         .....body......... 
      POP 
   POP 
POP 

and the ‘\root ...\of {...}’ construct

PUSH 
   ...root value... 
POP 
PUSH 
   PUSH 
      PUSH 
         ...root sign characters with PUSH-POP... 
      POP 
      PUSH 
         RIGHT4:   15213546 
         DOWN3:    -1553322 
         PUT_RULE: height: 26213 
                   length: 1197667 
         DOWN3:    1553322 
         PUSH 
            ...root content... 
         POP 
      POP 
   POP 
POP 

For instance,

$ 
\special{t4ht\string~} 
\special{t4ht=<msqrt>} 
%%%%%%%% 
\special{t4ht\string~!e<[BEFORE]} %   insert at start of path 
\special{t4ht\string~!e>[AFTER]}  %   insert at end of path 
\special{t4ht\string~!ee/}        %   ignore content within group 
\special{t4ht\string~!ese-}       %   ignore until rulers until next group 
%%%%%% 
\sqrt 1 
\special{t4ht\string~} 
$ 
 
$ 
\special{t4ht\string~}% 
\special{t4ht=<mroot>}% 
%%%%%%%% 
\special{t4ht\string~!see<[BEFORE]} 
\special{t4ht\string~!see>[AFTER]} 
\special{t4ht\string~!seee/} 
\special{t4ht\string~!seese-} 
%%%%%% 
\root 2\of 3% 
\special{t4ht\string~} 
$ 

Record Request

<..struct stack_entry..>+
 struct group_path * path_start, * path_end;
 -_-_-

<..types..>+
 struct group_path{
   U_CHAR action;
   U_CHAR *path;
   U_CHAR *info;
   struct group_path * next;
 };
 -_-_-

<..initialized entries for grouping stack..>+
 stack[i].path_start = (struct group_path *) 0;
 stack[i].path_end   = (struct group_path *) 0;
 -_-_-

<..store path-based forward submissions..>
                  struct group_path *p, *t;
                  U_CHAR            *q, str[256];
                  int               n;
 p = m_alloc(struct group_path,1);
 <.record action and path.>
 <.record info for path.>
 n = stack_n - 1;
 if( p->action == ’>’ ){
    p->next =  stack[ n ].path_end;
    stack[ n ].path_end = p;
 } else {
    p->next = (struct group_path *) 0;
    if( stack[n].path_start == (struct group_path *) 0 ) {
       stack[n].path_start = p;
    } else {
       t = stack[n].path_start;
       while( t->next != (struct group_path *) 0 ) { t = t->next; }
       t->next = p;
 }  }
 -_-_-

<..record action and path..>
 n = 0;
 while( --special_n ) {
    str[n] = get_char();
    if( ( str[n] != ’e’) && (str[n] != ’s’) ){ break; }
    n++;
 }
 if((
     ( str[n] != ’<’) && (str[n] != ’>’) &&
     ( str[n] != ’/’) && (str[n] != ’-’)
    ) || (n==0) ){
   str[n+1] = ’\0’;
   err_i_str(38,str);
 }
 p->action = str[n]; str[n] = ’\0’;
 p->path = m_alloc(char,n+1);
 (IGNORED) strcpy((char *) p->path, (char *) str);
 -_-_-

<..record info for path..>
 q = p->info = m_alloc(char,special_n+1);
 while( --special_n ) *q++ = get_char();
 *q = ’\0’;
 -_-_-

Process Path Lists At Entry To Group

<..path group at entry to group..>
 {
            <.vars for path processing.>
   if( <.radical-line-off.> ){
     <.end ignore spaces.>
     <.radical-line-off.> = FALSE;
   }
   if( stack_n > 1 ){
     p = stack[stack_n - 2].path_start;
     if(  p !=  (struct group_path *) 0 ){
       <.traverse path starts on entry.>
     }
     p = stack[stack_n - 2].path_end;
     if(  p !=  (struct group_path *) 0 ){
       <.traverse path ends on entry.>
     }
     <.connect the revised path lists.>
 } }
 -_-_-

<..vars for path processing..>
 struct group_path *start_head, *start_tail,
                   *parent_start_head, *parent_start_tail,
                   *end_head, *end_tail,
                   *parent_end_head, *parent_end_tail,
                   *p, *q;
 int place=0;
 start_head = start_tail = parent_start_head = parent_start_tail
            = end_head   = end_tail          = parent_end_head
            = parent_end_tail = (struct group_path *) 0;
 -_-_-

<..connect the revised path lists..>
   stack[stack_n - 1].path_start = start_head;
   stack[stack_n - 1].path_end   = end_head;
   stack[stack_n - 2].path_start = parent_start_head;
   stack[stack_n - 2].path_end   = parent_end_head;
 -_-_-

<..traverse path starts on entry..>
 while( p !=  (struct group_path *) 0 ){
    <.process a path-start at entry.>
    q = p;
    p = p->next;
    q->next = (struct group_path *) 0;
    <.update temporary start paths.>
 }
 -_-_-

<..traverse path ends on entry..>
 while( p !=  (struct group_path *) 0 ){
    <.process a path-end at entry.>
    q = p;
    p = p->next;
    q->next = (struct group_path *) 0;
    <.update temporary end paths.>
 }
 -_-_-

<..update temporary start paths..>
 switch( place ){
  case <.store at parent path-start.>:
    if( parent_start_head == (struct group_path *) 0 ){
        parent_start_head = parent_start_tail = q;
    } else {
        parent_start_tail = parent_start_tail->next = q;
    }
    break;
  case <.store at path-start.>:
    if( start_head == (struct group_path *) 0 ){
        start_head = start_tail = q;
    } else {
        start_tail = start_tail->next = q;
    }
    break;
  case <.delete path entry.>:
    <.delete path entry q.>
    break;
 }
 -_-_-

<..update temporary end paths..>
 switch( place ){
  case <.store at parent path-end.>:
    if( parent_end_head == (struct group_path *) 0 ){
        parent_end_head = parent_end_tail = q;
    } else {
        parent_end_tail = parent_end_tail->next = q;
    }
    break;
  case <.store at path-end.>:
    if( end_head == (struct group_path *) 0 ){
        end_head = end_tail = q;
    } else {
        end_tail = end_tail->next = q;
    }
    break;
  case <.delete path entry.>:
    <.delete path entry q.>
    break;
 }
 -_-_-

<..store at parent path-start..>
 0 -_-_-

<..store at parent path-end..>
 1 -_-_-

<..store at path-start..>
 2 -_-_-

<..store at path-end..>
 3 -_-_-

<..delete path entry..>
 4 -_-_-

<..delete path entry q..>
 free((void *)  q->path );
 free((void *)  q->info );
 free((void *)  q );
 -_-_-

Process Individual Records at Entry To Group

The ‘e’ records at the parent’s path-start are moved to the path-start list of the current group, with the leading ‘e’ removed from the path. The only exception is for paths of length 1 with actions ‘<’ and ‘-’. They contribute their content to the output, and then discarded.

The ‘s’ records at the parent’s path-start list stay at that list, with the ‘s’ character removed from the path.

The other records remain at the parent path-start list, to be removed at the end of that group.

<..process a path-start at entry..>
 if( *(p->path ) == ’e’ ) {
   (IGNORED) strcpy((char *) p->path, (char *) p->path+1);
   if( *(p->path) == ’\0’ ) {
      switch( p->action ){
        case ’<’:  print_f( p->info );
                   place = <.delete path entry.>;
                   break;
        case ’/’:  ignore_chs++;
                   place = <.store at path-start.>; break;
        case ’-’:  <.radical-line-off.> = TRUE;
                   <.ignore spaces.>
                   place = <.delete path entry.>; break;
      }
   } else {
      place = <.store at path-start.>;
   }
 } else {
   if( *(p->path ) == ’s’ ) {
      (IGNORED) strcpy((char *) p->path, (char *) p->path+1);
   }
   place = <.store at parent path-start.>;
 }
 -_-_-

The ‘s’ records at the parent’s path-end list stay at that list, with the ‘s’ character removed from the path.

The ‘e’ records are moved to the path-end list of the current group, with the leading ‘e’ removed from the path.

The other records are left intact at the parent’s path-end list.

<..process a path-end at entry..>
 if( *(p->path ) == ’e’ ) {
   (IGNORED) strcpy((char *) p->path, (char *) p->path+1);
   place = <.store at path-end.>;
 } else {
   if( *(p->path ) == ’s’ ) {
      (IGNORED) strcpy((char *) p->path, (char *) p->path+1);
   }
   place = <.store at parent path-end.>;
 }
 -_-_-

Path Group At Exit From Group

<..path group at exit from group..>
 {
      struct group_path *p, *q;
   if( stack_n > 1 ){
     p = stack[stack_n - 1].path_start;
     if(  p !=  (struct group_path *) 0 ){
       <.traverse path starts on exit.>
     }
     p = stack[stack_n - 1].path_end;
     if(  p !=  (struct group_path *) 0 ){
       <.traverse path ends on exit.>
 } } }
 -_-_-

<..traverse path starts on exit..>
 while( p !=  (struct group_path *) 0 ){
    <.process a path-start at exit.>
    q = p;
    p = p->next;
    <.delete path entry q.>
 }
 -_-_-

<..traverse path ends on exit..>
 while( p !=  (struct group_path *) 0 ){
    <.process a path-end at exit.>
    q = p;
    p = p->next;
    <.delete path entry q.>
 }
 -_-_-

Process Individual Records at Exit from Group

<..process a path-start at exit..>
 if( *(p->path) != ’\0’ ) {
    <.warn 38 for path.>
 } else  {
    switch( p->action ){
      case ’/’:  ignore_chs--;  break;
       default:  {
           <.warn 38 for path.>
           break;
 }  }  }
 -_-_-

<..process a path-end at exit..>
 if( *(p->path) != ’\0’ ) {
    <.warn 38 for path.>
 } else  {
    switch( p->action ){
      case ’>’:  print_f( p->info );  break;
       default:  {
           <.warn 38 for path.>
           break;
 }  }  }
 -_-_-

<..warn 38 for path..>
             char str[256];
 (IGNORED) strcpy(str, "...."); *(str+3) = p->action;
 (IGNORED) strct(str,p->info); warn_i_str(38,str);
 -_-_-