This function first figures out which field we're in on the current line, and then it tries to find the last previous encounter line that has that same field, and duplicates that value onto the current line.
;; - - - i b p - d i t t o - o n c e - - -
(defun ibp-ditto-once ()
"Duplicate a field from the corresponding field of the previous tail.
[ if there are no previous lines with tails ->
error/exit
else if point is not at end of line, or beyond all tail fields ->
error/exit
else ->
buffer := buffer with text appended after point
copied from the corresponding tail position of
the last previous line with a tail
point := point advanced to the end of the field containing
point ]
----------------------------------------------------------------"
Here is an illustration of the process.

The previous line is the top box in this figure, and its
layout is described in an ibp-line-object named
prev. The current line's layout is
described by an ibp-line-object named line. Note that the previous line's head
portion may be a different size than the head of the
current line, e.g., an N-type encounter line can ditto a
field from a G-type line.
The hatched portion represents content in the current
field already entered, if any. Variables dup-beg and dup-end bracket the
portion of the previous line that needs to be copied to
the current line. Variable dup-string
will contain the actual text to be copied.
First we open a let scope and define the
local variables. These variables are as illustrated
above.
(let (line ;; ibp-line-object for the line containing point
prev ;; ibp-line-object for last prev. line with a tail
field ;; ibp-field-object for the field containing point
dup-beg ;; Start position of string to be duplicated
dup-end ;; End position of string to be duplicated
dup-len ;; Length of string to be duplicated
dup-string ;; String to be duplicated
tail-off) ;; Offset relative to tail of point
First we find the parts of the current line using
Section 5.6, “ibp-analyze-line: Where are the parts of
the current line?”.
;; [ line := a ibp-line-object representing the line
;; containing point ]
(setq line (ibp-analyze-line))
Duplication is valid only at the end of the line. It is not valid except on encounter lines.
;; [ if (line.kind is 'non-trans)
;; or (point is not at end of line) ->
;; error/exit
;; else -> I ]
(if (or (eq (ibp-line-kind line) 'non-trans)
(/= (point) (ibp-line-end line)))
(error "Duplication is valid only at the end of a line."))
Next we need to find the line from which we are copying.
One nice feature of this process is that it searches back
through the buffer until it finds a transaction line; a
ibp-line-object representing that line is
stored in prev. This searching is done by
Section 5.14, “ibp-find-prev-trans: Find the last
preceding line with a tail”; this could
fail, in which case that function returns null.
;; [ if there is at least one line with a tail preceding the line
;; containing point ->
;; prev := an ibp-line-object representing that line
;; else -> error/exit ]
(setq prev (ibp-find-prev-trans))
(if (null prev)
(error "No previous line to duplicate."))
Next we need to find the field in the current line
containing the cursor, and set field to an
ibp-field-object describing that field.
This is done by Section 5.9, “ibp-bracket-field: What field contains a
given position?”; if
the cursor is beyond all the tail fields, that function
returns null.
;; [ if point is in the head part of line ->
;; field := an ibp-field-object representing the head,
;; with nil filler
;; if point is in a tail field ->
;; field := an ibp-field-object representing that field
;; if point is beyond all tail fields ->
;; error/exit ]
(setq field (ibp-bracket-field line (point)))
(if (null field)
(error "You are beyond the fields we know."))
Now we want to find the part of prev
corresponding to the field we are duplicating, and set
dup-string to the part to be copied.
First we compute tail-off, the distance
into the tail where we want to start copying. Then we
bracket the corresponding field in prev
between dup-beg and dup-end. If dup-end is past the end of the
previous line, that's an error.
;; [ if prev does not have characters corresponding to [line.end:
;; field.end] ->
;; error/exit
;; else ->
;; dup-string := characters from prev whose position relative to
;; prev's tail correspond to characters [line.end:
;; field.end] ]
(setq tail-off
(- (ibp-line-end line) (ibp-line-tail line)))
(setq dup-beg (+ (ibp-line-tail prev) tail-off))
(setq dup-len (- (ibp-field-end field) (ibp-line-end line)))
(setq dup-end (+ dup-beg dup-len))
(if (> dup-end (ibp-line-end prev))
(error "Previous line is too short to duplicate."))
(setq dup-string (buffer-substring dup-beg dup-end))
All that remains is to insert the duplicated text before the cursor. The cursor is left at the end of the insertion.
;; [ buffer := buffer with dup-string inserted before point ]
(insert dup-string)))