%option "-nons-low-nocheck-nodiag"

%systemroutine copy subprocess data

! This should be called by the child process, once only.
! Copies are made of the own areas of all modules comprising the current
! program, with all the external linkages being adjusted accordingly.
! The effect is that after the call, the own/external variables used by
! parent and child processes will be independent.

%recordformat fe02 object fm -
  (%half flags,extra,%integer type,offset,%string(255)name)
%recordformat fe02 header fm -
  ({%byte type,version) %or}%half tyver,flags,export,import,%integer codesize,
   %half reset,main, %integer ownsize,stack,%half dlim,chlim,spare1,spare2)
%recordformat mar fm -
  (%record(mar fm)%name next, %record(fe02 header fm)%name header,
   %integer gla,%string(255)filename)
%recordformat par fm -
  (%record(par fm)%name next,
   %record(mar fm)%name modules)
%recordformat fe02 indir fm -
  (%integer address %or -
   %half op1, %integer opd1, %half op2, %integer opd2)
%recordformat dyn fm -
  (%record(fe02 object fm)%name object, %record(par fm)%name program)

@16_3e88 %integerfn heapget(%integer bytes)
@372(a5) %record (par fm)%name topprog
%constinteger procmask=16_3000

%record(fe02objectfm)%map nextobject(%record(fe02objectfm)%name o)
! Get at the next (i.e. adjacent) external object descriptor
! within the current export or import section.
  %result == record((addr(o[1])-255+length(o_name)+1)&\1)
%end

%record(*)%map reverse(%record(*)%name list)
! Reverse an arbitrary singly-linked list, provided the FIRST
! field of the records involved are used for linking.
%recordformat f(%record(f)%name next)
%record(f)%name l == list
%record(f)%name s == nil,c
  %while l##nil %cycle
    c == l; l == c_next; c_next == s; s == c
  %repeat
  %result == s
%end

%routine relocate module(%record(marfm)%name new,old)
! Fix up cross-references in the external linkage, following duplication
! of a module's own area.  OLD and NEW refer to descriptors of the same
! module, except that their respective GLA fields are different.
%integer oldarea,newarea,limit
%record(marfm)%name mar
%record(fe02headerfm)%name hdr
%record(fe02objectfm)%name obj
%record(fe02indirfm)%name ind
  %returnif old==nil %or new==nil              {safety check}
  oldarea = old_gla; newarea = new_gla
  hdr == old_header; %returnif hdr_ownsize<=0  {no data area}
  limit = oldarea+hdr_ownsize
  %returnunless hdr==new_header %and old_filename=new_filename {safety}
  mar == topprog_modules  {scan all active modules to see if they import}
  %while mar##nil %cycle  {any external objects from the current module}
    hdr == mar_header
    %if hdr_import#0 %start
      obj == record(addr(hdr[1])+hdr_export)
      %while obj_flags#0 %cycle
        ind == record(mar_gla+obj_offset)
        %if obj_flags&procmask=0 %start          {i.e. a data object}
          %if oldarea<=ind_address<limit %start  {from this module}
            ind_address = ind_address-oldarea+newarea  {adjust}
          %finish
        %elseif ind_opd1=oldarea   {external code object}
          ind_opd1 = newarea       {replace GLA used}
        %finish
        obj == nextobject(obj)
      %repeat
    %finish
    mar == mar_next
  %repeat
  a4 = newarea %if a4=oldarea  {not forgetting this as well}
%end

%record(marfm)%map duplicate module list(%record(marfm)%name oldlist)
! Make a copy of the module activation list for the current program.
! At the same time duplicate and copy over any data areas.
%record(marfm)%name newlist==nil,item
%record(fe02headerfm)%name hdr
%integer oldarea,newarea,count
  %while oldlist##nil %cycle
    item == record(heapget(sizeof(item)-255+length(oldlist_filename)))
    hdr == oldlist_header; item_header == hdr
    oldarea = oldlist_gla; newarea = oldarea; count = hdr_ownsize
    %if count>0 %start
      newarea = heapget(count)
      %while count>0 %cycle
        count = count-1
        byte(newarea+count) = byte(oldarea+count)
      %repeat
    %finish
    item_gla = newarea
    item_filename = oldlist_filename
    oldlist == oldlist_next; item_next == newlist; newlist == item
  %repeat
  %result == reverse(newlist)
%end

! Main part of COPY SUBPROCESS DATA

%record(parfm)%name par
%record(marfm)%name original,mar

  par == new(par)          {duplicate (only first cell in) program list}
  par_next == topprog_next
  original == topprog_modules
  par_modules == duplicate module list(original)
  topprog == par
  mar == topprog_modules  {now relocate all modules in the copied list}
  %while mar##nil %cycle
    relocate module(mar,original)
    mar == mar_next; original == original_next
  %repeat
%end
