/****************************************************************************
                   Microsoft RPC Version 1.0
                Copyright Microsoft Corp. 1992
                        xmit Example

    FILE:       xmitu.c

    PURPOSE:    Utility functions used by both client and server
                sides of the RPC distributed application.
                This sample demonstrates the transmit_as example.
                A doubly-linked list is transmitted over the network
                as a sized array.

    RELATED:    xmits.c - server main
                xmitp.c - remote procedures
                xmitc.c - client main

    FUNCTIONS:  DOUBLE_LINK_TYPE_to_xmit - convert list to array
                DOUBLE_LINK_TYPE_from_xmit - convert array to list
                DOUBLE_LINK_TYPE_free_inst - free linked list memory
                DOUBLE_LINK_TYPE_free_xmit - free array memory
                midl_user_allocate - user-supplied memory allocator
                midl_user_free - user-supplied routine to free memory

                ArrayWalkProc - utility to display the array
                ListWalkProc - utility to display the linked list
                InsertNewNode - utility to add a node to the list

    COMMENTS:   This sample program generates a linked list to
                demonstrate how a list with aliasing can be transmitted
                using the transmit_as attribute as a sized array.
                The pointers are rebuilt on the server side.

****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include "xmit.h"    // header file generated by MIDL compiler
#include "xmitu.h"


/***************************************************************************/

void ArrayWalkProc(DOUBLE_XMIT_TYPE * pArray)
{
    int i;
    
    printf("Display contents of transmitted array:\n");
    for (i = 0; i < pArray->sSize; i++)
        printf("pArray->asNumber[%d] = %d\n", i, pArray->asNumber[i]);
}

void ListWalkProc(DOUBLE_LINK_TYPE * pList)
{
    printf("Display contents of doubly linked list:\n");
    while (pList != NULL) {
        printf("pList @0x%lx = %d, Next = 0x%lx\n", 
               pList, pList->sNumber, pList->pNext);  
        pList = pList->pNext;
    }
}

DOUBLE_LINK_TYPE * InsertNewNode(short sValue, DOUBLE_LINK_TYPE * pPrevious)
{
    DOUBLE_LINK_TYPE * pNew;

    do {
        pNew = (DOUBLE_LINK_TYPE *)midl_user_allocate(sizeof(DOUBLE_LINK_TYPE));
    } while (pNew == pPrevious);

    pNew->pNext = NULL;      // initialize 
    pNew->pPrevious = NULL;  // initialize 
    pNew->sNumber = sValue;  // insert b between a and c 
    
    pNew->pPrevious = pPrevious;             // prev(b) = a 
    if (pPrevious == NULL) 
        pNew->pNext = NULL;   
    else {
        pNew->pNext = pPrevious->pNext;      // next(b) = c
        pPrevious->pNext = pNew;             // next(a) = b
        if (pNew->pNext != NULL)
            (pNew->pNext)->pPrevious = pNew; // prev(c) = b 
    }
    
    return(pNew);
}


/***************************************************************************/

void __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}


/***************************************************************************/

/* convert from linked list to array */
void __RPC_API 
DOUBLE_LINK_TYPE_to_xmit(
    DOUBLE_LINK_TYPE __RPC_FAR * pList,
    DOUBLE_XMIT_TYPE __RPC_FAR * __RPC_FAR * ppArray)
{
    short cCount = 0;
    DOUBLE_LINK_TYPE * pHead = pList;  // save pointer to start 
    DOUBLE_XMIT_TYPE * pArray;

    /* count the number of elements to allocate memory */
    for (; pList != NULL; pList = pList->pNext)
        cCount++;

    /* allocate the memory for the array */
    pArray = (DOUBLE_XMIT_TYPE *) midl_user_allocate 
             (sizeof(DOUBLE_XMIT_TYPE) + (cCount * sizeof(short)));
    pArray->sSize = cCount;

    /* copy the linked list contents into the array */
    for (cCount = 0, pList = pHead; pList != NULL; pList = pList->pNext)
        pArray->asNumber[cCount++] = pList->sNumber;

    /* return the address of the pointer to the array */
    *ppArray = pArray;
}

/* convert from array to linked list */
void __RPC_API 
DOUBLE_LINK_TYPE_from_xmit( 
    DOUBLE_XMIT_TYPE __RPC_FAR * pArray,
    DOUBLE_LINK_TYPE __RPC_FAR * pList)
{
    DOUBLE_LINK_TYPE *pCurrent;
    int i;

    if (pArray->sSize <= 0) {  // error checking
        pList = NULL;
        return;
    }

    if (pList == NULL)  
        pList = InsertNewNode(pArray->asNumber[0], NULL);             
    else {    
        DOUBLE_LINK_TYPE_free_inst(pList);  // free all other nodes
        pList->sNumber = pArray->asNumber[0];
        pList->pNext = NULL;
    }

    pCurrent = pList;
    for (i = 1; i < pArray->sSize; i++)  // write new values 
        pCurrent = InsertNewNode(pArray->asNumber[i], pCurrent);
}


/* free the doubly linked list */
/* move forward through list, freeing the previous entry */
void __RPC_API 
DOUBLE_LINK_TYPE_free_inst(
    DOUBLE_LINK_TYPE __RPC_FAR * pList)
{
    while (pList->pNext != NULL)  // go to end of list
        pList = pList->pNext;

    pList = pList->pPrevious;
    while (pList != NULL) {  // back through list
        midl_user_free(pList->pNext);
        pList = pList->pPrevious;
    }
}

/* free the array structure */
void __RPC_API 
DOUBLE_LINK_TYPE_free_xmit(
    DOUBLE_XMIT_TYPE __RPC_FAR * pArray)
{
    midl_user_free(pArray);
}

/* end file xmitu.c */
