The first task is to find out whether more data exists, so that this Boolean information can be serialized. Notice that this statement is unnecessary in the XDR_DECODE case, because the value of more_data is not known until you deserialize it in the next statement.
The next statement implements XDR on the more_data field of the XDR union. Then if no more data exists, you set this last pointer to NULL to indicate the end of the list, and return TRUE because you are done. Note that setting the pointer to NULL is only important in the XDR_DECODE case, because the pointer is already NULL in the XDR_ENCODE and XDR_FREE cases.
Next, if the direction is XDR_FREE, set the value of nextp to indicate the location of the next pointer in the list. You set this value now because you need to dereference gnp to find the location of the next item in the list. After the next statement, the storage pointed to by gnp is freed up and no longer valid. You cannot set this value for all directions, though, because in the XDR_DECODE direction the value of gnp is not set until the next statement.
Next, you use XDR on the data in the node using the primitive xdr_reference(). xdr_reference() is like xdr_pointer(), which you used before, but it does not send over the Boolean indicating whether more data exists. You use xdr_reference() instead of xdr_pointer() because you have already used XDR on this information yourself.
Notice that the XDR routine passed is not the same type as an element in the list. The routine passed is xdr_gnumbers(), but each element in the list is actually of type gnumbers_node. You don't pass xdr_gnumbers_node() because it is recursive. Instead, use xdr_gnumbers(), which uses XDR on all of the nonrecursive part. Note that this trick works only if the gn_numbers field is the first item in each element, so that their addresses are identical when passed to xdr_reference().
Finally, you update gnp to point to the next item in the list. If the direction is XDR_FREE, you set it to the previously saved value. Otherwise, you can dereference gnp to get the proper value. Though harder to understand than the recursive version, this nonrecursive routine runs more efficiently because much of the procedure call overhead has been removed. Most lists are small, in the hundreds of items or less, and the recursive version should be sufficient for them.