AppMain.c

       1  /******************************************************************************
           *
           * Palm Interactive Boardroom (  PIB )
           * Handheld presentation tool using a networked projector as a display.
           *
           * This source code is based on default IDE source as well as royalty-free
           * samples provided with the IDE. This file shell was generated by the Palm OS
           * Developer Suite IDE.
           *
           * Other royalty-free source code from HelloNetLib.c (  provided by
           * Palm at www.palmsource.com ) to send TCP and UDP traffic to an IP
           * address + port number was modified significanlty in support of
           * the PIB requirements.
           *
           * Programming environment: Palm OS Developer Suite
           *
           * Created by Scott Armstrong for UIUC CS-427 Group 30,   Fall 2005
           * based on UML and requirements outlined by Group 30 members.
           *
           * File: AppMain.c
           *
           * Version 3.2.1
           * - Work on integrating "Transfer" function set (  Project )
           * - "Connect" (  connect to network ) is automatically started on the device
           * based on a network call - an interactive function will be integrated
           * in future iterations.
           * - Issues connecting the Button trigger event to the Transfer form
           *
           *****************************************************************************/
          
          #include <PalmOS.h>
          //#include <StdIOPalm.h> // For console IO,   link SdtIOPalm.c (  possible future use )
          #include <INetMgr.h> // For managing device network settings
          
          #include "AppResources.h" // Resources to associate forms to function calls
          
          
          /***********************************************************************
           *
           * Internal Structures
           *
           ***********************************************************************/
          
          typedef struct // for Network IP address + Port
          {
           NetIPAddr addr;
           UInt16 port;
          } NetLibPreferenceType;
          
          
          /***********************************************************************
           *
           * Globals
           *
           ***********************************************************************/
          
          NetLibPreferenceType gPrefs = {};
          const UInt16 bufLen = 16; // for initial test string to send (  temporary )
          char buf[16] = "testing 1-2\n"; // initial test string to send (  temporary )
          
          
          /***********************************************************************
           *
           * Internal Constants,   application-specific
           *
           ***********************************************************************/
          
          #define appFileCreator 'xPIB' // register your own at http://www.palmos.com/dev/creatorid/
          // CRIDs xPIB and cPIB are reserved for our use at palmsource.com
          #define appVersionNum 0x01 // - version set on:
          #define appPrefID 0x00 // - prefID set on:
          #define appPrefVersionNum 0x01 // - PrefVersion set on:
          // Minimum OS version supported - Network issues
          #define ourMinVersion sysMakeROMVersion(  3,  0,  0,  sysROMStageRelease,  0 )
          #define kPalmOS10Version sysMakeROMVersion(  1,  0,  0,  sysROMStageRelease,  0 )
          
          #define CLEAR_RESULT -1
          #define PROCESSING_RESULT -2
          
          
          /***********************************************************************
           *
           * Internal Functions
           *
           ***********************************************************************/
          
          // User Interface functions (  Transfer/Project Form )
      88  static void ShowResult(  UInt16 id,   Err error );
      89  static void ClearResults(  void );
      90  static Boolean GetValue(  UInt16 objID,   Int32 *valueP );
      91  static Boolean SetValue(  UInt16 objID,   Int32 value );
      92  static Boolean GetAddr(  UInt16 AppNetRefnum,   NetIPAddr *addrP,   UInt16 *portP );
          
          // Network function
      95  static void SendTCP(  void );
          // static void SendUDP(  void ); // Currently only using TCP - possible future
           // UDP implementation
          
          /***********************************************************************
           *
           * Internal Function Definitions
           *
           ***********************************************************************/
          
          /***********************************************************************
           *
           * FUNCTION: ShowResult (  part of Transfer / Project )
           *
           * DESCRIPTION: This routine updates the UI to show the results of each
           * of the function calls involved in the current network
           * operation.
           * This information should be streamlined or possibly removed
           * from the UI as we approach the final PIB releases.
           * This is valuable information for testing and debugging for now.
           *
           * PARAMETERS: id - ID of the label to update
           * error - error code returned by the corresponding
           * function. If this is -1 the result is cleared
           *
           * RETURNED: none
           *
           ***********************************************************************/
     123  static void ShowResult(  UInt16 id,   Err error )
          {
           char resultString[11];
           FormType *formPtr;
           UInt16 objIndex;
          
           // Prepare the result as a string
           switch (  error ){
           case CLEAR_RESULT:
           StrCopy(  resultString,   " " );
           break;
           case PROCESSING_RESULT:
           StrCopy(  resultString,   " .... " ); // indicates no results yet
           break;
           default:
           StrPrintF(  resultString,   "0x%04X",   error );
           break;
           }
          
           // Update the specified field label
           formPtr = FrmGetFormPtr(  MainForm );
           objIndex = FrmGetObjectIndex(  formPtr,   id );
           FrmHideObject(  formPtr,   objIndex );
           FrmCopyLabel(  formPtr,   id,   resultString );
           FrmShowObject(  formPtr,   objIndex );
          }
          
          /***********************************************************************
           *
           * FUNCTION: ClearResults
           *
           * DESCRIPTION: This routine clears all the results listed on the main
           * form.
           *
           * PARAMETERS: none
           *
           * RETURNED: none
           *
           ***********************************************************************/
     162  static void ClearResults(  void )
          {
           ShowResult(  MainSysLibFindResultLabel,   -1 );
           ShowResult(  MainNetLibOpenResultLabel,   -1 );
           ShowResult(  MainNetLibOpenIFResultLabel,   -1 );
           ShowResult(  MainNetLibSocketOpenResultLabel,   -1 );
           ShowResult(  MainNetLibSocketConnectResultLabel,   -1 );
           ShowResult(  MainNetLibSendResultLabel,   -1 );
           ShowResult(  MainBytesSentResultLabel,   -1 );
           ShowResult(  MainNetLibSocketCloseResultLabel,   -1 );
           ShowResult(  MainNetLibCloseResultLabel,   -1 );
          }
          
          /***********************************************************************
           *
           * FUNCTION: GetValue
           *
           * DESCRIPTION: This routine gets the numeric value of the specified
           * text field.
           *
           * PARAMETERS: objID - ID of the field on the main form
           * valueP - pointer to memory where value is returned
           *
           * RETURNED: True if successful,   false otherwise.
           *
           ***********************************************************************/
     188  static Boolean GetValue(  UInt16 objID,   Int32 *valueP )
          {
           FormType *formPtr;
           UInt16 objIndex;
           FieldType *fieldPtr;
           Char *text;
          
           formPtr = FrmGetFormPtr(  MainForm );
           if (  !formPtr ) return false;
          
           objIndex = FrmGetObjectIndex(  formPtr,   objID );
           fieldPtr = (  FieldType* )FrmGetObjectPtr(  formPtr,   objIndex );
           if (  !fieldPtr ) return false;
          
           text = FldGetTextPtr(  fieldPtr );
           if (  !text ) return false;
          
           *valueP = StrAToI(  text );
           return true;
          }
          
          
          
          /***********************************************************************
           *
           * FUNCTION: SetValue
           *
           * DESCRIPTION: This routine sets the numeric value of the specified
           * text field.
           *
           * PARAMETERS: objID - ID of the field on the main form
           * value - the value to insert
           *
           * RETURNED: True if successful,   false otherwise.
           *
           * NOTES: Determines the maximum length to use based upon the ID
           *
           ***********************************************************************/
     226  static Boolean SetValue(  UInt16 objID,   Int32 value )
          {
           FormType *formPtr;
           UInt16 objIndex;
           FieldType *fieldPtr;
           MemHandle h,   oldHandle;
           Char *text;
          
           formPtr = FrmGetFormPtr(  MainForm );
           if (  !formPtr ) return false;
          
           objIndex = FrmGetObjectIndex(  formPtr,   objID );
           fieldPtr = (  FieldType* )FrmGetObjectPtr(  formPtr,   objIndex );
           if (  !fieldPtr ) return false;
          
           if (  MainPortField == objID ) {
           h = MemHandleNew(  6 );
           } else {
           h = MemHandleNew(  4 );
           }
           if (  !h ) return false;
          
           text = MemHandleLock(  h );
           if (  MainPortField == objID ) {
           StrPrintF(  text,   "%u",   (  UInt16 )value );
           } else {
           StrPrintF(  text,   "%u",   (  UInt8 )value );
           }
           MemHandleUnlock(  h );
           oldHandle = FldGetTextHandle(  fieldPtr );
           FldSetTextHandle(  fieldPtr,   h );
           if (  oldHandle ) MemHandleFree(  oldHandle );
          
           return true;
          }
          
          /***********************************************************************
           *
           * FUNCTION: GetAddr
           *
           * DESCRIPTION: This routine gets the IP address and port that the user
           * has entered.
           *
           * PARAMETERS: AppNetRefnum - Reference number of NetLib
           * addrP - pointer to memory where address is
           * returned
           * portP - pointer to memory where port is
           * returned
           *
           * RETURNED: True if the IP address and port are valid,   false
           * otherwise.
           *
           * NOTES: The only check peformed is to see whether the values
           * are in the appropriate numeric ranges. This doesn't
           * check whether the specified host exists or is listening
           * on the specified port.
           * If host is not found,   the system stops processing and
           * pauses until a timeout period.
           * - Future releases should include a "host not found" check.
           *
           ***********************************************************************/
     287  static Boolean GetAddr(  UInt16 AppNetRefnum,   NetIPAddr *addrP,   UInt16 *portP )
          {
           UInt8 i;
           Int32 ip[4];
           Int32 port;
           Char textIP[16];
          
           // Get the values
           for (  i = 0; i < 4; i++ ) {
           if (  !GetValue(  MainIP1Field + i,   &(  ip[i] ) ) ||
           ip[i] > 255 ||
           ip[i] < 0 ) {
           return false;
           }
           }
          
           if (  !GetValue(  MainPortField,   &port ) ||
           port > 65535 ||
           port < 1 ) {
           return false;
           }
          
           StrPrintF(  textIP,   "%u.%u.%u.%u",   (  UInt8 )ip[0],   (  UInt8 )ip[1],   (  UInt8 )ip[2],   (  UInt8 )ip[3] );
           *addrP = NetLibAddrAToIN(  AppNetRefnum,   textIP );
           *portP = (  UInt16 )port;
           return true;
          }
          
          
          /***********************************************************************
           *
           * FUNCTION: SendTCP
           *
           * DESCRIPTION: This routine uses NetLib to send TCP data.
           * The user needs to enter the appropriate IP address and Port.
           * Future versions should include selectable Ports depending on
           * the Wireless projector configuation or a default (  if one exists ).
           *
           * PARAMETERS: none
           *
           * RETURNED: none
           *
           ***********************************************************************/
     330  static void SendTCP(  void )
          {
           Err error;
           UInt16 AppNetRefnum;
           UInt16 ifErrs;
           NetSocketRef socket;
           Int16 result;
           NetSocketAddrINType destAddr;
          // const UInt16 bufLen = 15;
          // char buf[bufLen] = "xxxxxx\n"; // will need to substitute a file
           // earlier attempts to compile this with
           // a locally scoped Char array wouldn't compile.
           // this function currently uses a globally
           // scoped Char array for testing
           UInt16 sentBytes = 0;
          
           ClearResults(   );
          
           // Find the network library and activate the currenlty configured network.
           ShowResult(  MainSysLibFindResultLabel,   PROCESSING_RESULT );
           error = SysLibFind(  "Net.lib",   &AppNetRefnum );
           ShowResult(  MainSysLibFindResultLabel,   error );
           if (  error ) return;
          
           // Get and validate the destination
           if (  !GetAddr(  AppNetRefnum,   &gPrefs.addr,   &gPrefs.port ) ) {
           FrmAlert(  InvalidDestAlert );
           return;
           }
          
           // Open the network library
           ShowResult(  MainNetLibOpenResultLabel,   PROCESSING_RESULT );
           ShowResult(  MainNetLibOpenIFResultLabel,   PROCESSING_RESULT );
           error = NetLibOpen(  AppNetRefnum,   &ifErrs );
           ShowResult(  MainNetLibOpenResultLabel,   error );
           ShowResult(  MainNetLibOpenIFResultLabel,   ifErrs );
           if (  ifErrs || (  error && error != netErrAlreadyOpen ) ) goto CloseNetLib;
          
           // Open a socket
           ShowResult(  MainNetLibSocketOpenResultLabel,   PROCESSING_RESULT );
           socket = NetLibSocketOpen(  AppNetRefnum,   // Network library
           netSocketAddrINET,   // Address domain
           netSocketTypeStream,   // Socket type
           netSocketProtoIPTCP,   // Protocol
           -1,   // Timeout
           &error // Error result
            );
           ShowResult(  MainNetLibSocketOpenResultLabel,   error );
           if (  error ) goto CloseNetLib;
          
           // Connect the socket to its destination
           MemSet(  &destAddr,   sizeof(  destAddr ),   0 );
           destAddr.family = netSocketAddrINET; // This should match the second argument to NetLibSocketOpen
           destAddr.port = gPrefs.port;
           destAddr.addr = gPrefs.addr;
           error = 0;
           ShowResult(  MainNetLibSocketConnectResultLabel,   PROCESSING_RESULT );
           result = NetLibSocketConnect(  AppNetRefnum,   // Network library
           socket,   // Socket reference
           (  NetSocketAddrType* )&destAddr,   // Destination address
           sizeof(  destAddr ),   // Length of destAddr
           -1,   // Timeout
           &error // Error result
            );
           if (  result == -1 ) {
           ShowResult(  MainNetLibSocketConnectResultLabel,   error );
           goto CloseSocket;
           } else {
           ShowResult(  MainNetLibSocketConnectResultLabel,   0 );
           }
          
           // Send data
           ShowResult(  MainBytesSentResultLabel,   PROCESSING_RESULT );
           while (  sentBytes < bufLen ) {
           ShowResult(  MainNetLibSendResultLabel,   PROCESSING_RESULT );
           result = NetLibSend (  AppNetRefnum,   // Network library
           socket,   // Socket reference
           buf + sentBytes,   // Buffer to send
           bufLen - sentBytes,   // Bytes to send from buffer
           0,   // Flags
           NULL,   // Destination address -- does not apply to TCP sockets
           0,   // Length of destination address
           -1,   // Timeout
           &error // Error result
            );
           if (  result == -1 ) {
           ShowResult(  MainNetLibSendResultLabel,   error );
           goto CloseSocket;
           }
           sentBytes += result;
           ShowResult(  MainBytesSentResultLabel,   sentBytes );
           }
           ShowResult(  MainNetLibSendResultLabel,   0 );
          
           // Close the socket
           CloseSocket:
           ShowResult(  MainNetLibSocketCloseResultLabel,   PROCESSING_RESULT );
           result = NetLibSocketClose (  AppNetRefnum,   // Network Library
           socket,   // Socket reference
           -1,   // Timeout
           &error // Error result
            );
           if (  result == -1 ) {
           ShowResult(  MainNetLibSocketCloseResultLabel,   error );
           } else {
           ShowResult(  MainNetLibSocketCloseResultLabel,   0 );
           }
          
           // Close the network library
           CloseNetLib:
           ShowResult(  MainNetLibCloseResultLabel,   PROCESSING_RESULT );
           error = NetLibClose(  AppNetRefnum,   false );
           ShowResult(  MainNetLibCloseResultLabel,   error );
          }
          
          /***********************************************************************
           *
           * FUNCTION: GetObjectPtr
           *
           * DESCRIPTION: This routine returns a pointer to an object in the current
           * form.
           *
           * PARAMETERS: formId - id of the form to display
           *
           * RETURNED: void *
           *
           * REVISION HISTORY:
           * - V3.2 - Defined but not used
           *
           *
           ***********************************************************************/
     461  static void * GetObjectPtr(  UInt16 objectID )
          {
           FormPtr frmP;
          
           frmP = FrmGetActiveForm(   );
           return FrmGetObjectPtr(  frmP,   FrmGetObjectIndex(  frmP,   objectID ) );
          }
          
          
          
          /***********************************************************************
           *
           * FUNCTION: MainFormInit
           *
           * DESCRIPTION: This routine initializes the MainTransferForm form.
           *
           * PARAMETERS: frm - pointer to the MainTransferForm form.
           *
           * RETURNED: nothing
           *
           * REVISION HISTORY:
           *
           *
           ***********************************************************************/
     485  static void MainFormInit(  FormPtr frmP )
          {
           ClearResults(   );
          }
          
          // =================================================================
          
          /***********************************************************************
           *
           * FUNCTION: MainTransferFormHandleEvent
           *
           * DESCRIPTION: This routine is the event handler for the
           * "MainTransferForm" of this application.
           *
           * PARAMETERS: eventP - a pointer to an EventType structure
           *
           * RETURNED: true if the event has handle and should not be passed
           * to a higher level handler.
           *
           * REVISION HISTORY:
           * - V3.2 - Defined byt not used
           * - Need to standardize the pointers used within the functions to
           * match the Palm defaults pEvent,   pForm
           * (  Semantically,   "p"ointer to Form,   not form"p"ointer )
           *
           ***********************************************************************/
     511  static Boolean MainTransferFormHandleEvent(  EventPtr eventP )
          {
           Boolean handled = false;
           FormPtr frmP;
          
           switch (  eventP->eType ) {
          
           case frmOpenEvent:
           frmP = FrmGetActiveForm(   );
           MainFormInit(   frmP );
          
           // Load initial values from prefs
           SetValue(  MainIP1Field,   (  gPrefs.addr & 0xFF000000 ) >> 24 );
           SetValue(  MainIP2Field,   (  gPrefs.addr & 0x00FF0000 ) >> 16 );
           SetValue(  MainIP3Field,   (  gPrefs.addr & 0x0000FF00 ) >> 8 );
           SetValue(  MainIP4Field,   gPrefs.addr & 0x000000FF );
           SetValue(  MainPortField,   gPrefs.port );
          
           FrmDrawForm (  frmP );
           handled = true;
           break;
          
           case frmUpdateEvent:
           break;
          
           case ctlSelectEvent:
           switch (  eventP->data.ctlSelect.controlID ) {
           case MainSendTCPButton:
           SendTCP(   );
           break;
          // case MainSendUDPButton: // possible future use
          // SendUDP(   );
          // break;
           default:
           break;
           }
           break;
          
           default:
           break;
           }
          
           return handled;
          }
          
          
          // =================================================================
          /***********************************************************************
           *
           * FUNCTION: MainFormDoCommand
           *
           * DESCRIPTION: This routine performs the menu command specified.
           *
           * PARAMETERS: command - menu item id
           *
           * RETURNED: nothing
           *
           * REVISION HISTORY: (  default menu item selector )
           * V3.0 - added "ConnectForm" stub form to Options menu
           * v3.1 - introduced network Send menu
           * V3.2 - removed "ConnectForm" stub form from Options menu
           *
           *
           ***********************************************************************/
     575  static Boolean MainFormDoCommand(  UInt16 command )
          {
           Boolean handled = false;
           FormType * pForm;
          
           switch (  command ) {
           case MainOptionsAboutStarterApp:
           pForm = FrmInitForm(  AboutForm );
           FrmDoDialog(  pForm ); // Display the About Box.
           FrmDeleteForm(  pForm );
           handled = true;
           break;
           case MainOptionsSelectFileStarterApp:
           pForm = FrmInitForm(  SelectFileForm );
           FrmDoDialog(  pForm ); // Display the Select File Box.
           FrmDeleteForm(  pForm );
           handled = true;
           break;
           case MainOptionsTransferSettingsStarterApp:
           pForm = FrmInitForm(  MainTransferForm );
           FrmDoDialog(  pForm ); // Display the Transfer Video Box.
           FrmDeleteForm(  pForm );
           handled = true;
           break;
           }
          
           return handled;
          }
          
          
          /***********************************************************************
           *
           * FUNCTION: MainFormHandleEvent
           *
           * DESCRIPTION: This routine is the event handler for the
           * "MainForm" of this application.
           *
           * PARAMETERS: pEvent - a pointer to an EventType structure
           *
           * RETURNED: true if the event has handle and should not be passed
           * to a higher level handler.
           *
           * REVISION HISTORY: (  default main form event handler )
           *
           *
           ***********************************************************************/
     621  static Boolean MainFormHandleEvent(  EventType* pEvent )
          {
           Boolean handled = false;
           FormType* pForm;
          
           switch (  pEvent->eType ) { // Level 1
           case menuEvent:
           return MainFormDoCommand(  pEvent->data.menu.itemID );
          
           case frmOpenEvent:
           pForm = FrmGetActiveForm(   );
           FrmDrawForm(  pForm );
           handled = true;
           break;
          
          // --location of the Trigger event from the main page using the Project
          // button. The functionality is currently separated as its own application
          // but can be re-integrated once the button trigger event works correctly.
          // --challenge: unfamiliar programming environment,   how to correctly
          // handle various triggers on a form.
          // --the form itself is now accessible using the Options menu but has no
          // functionality.
          //
          // case ctlSelectEvent:
          // switch (  pEvent->data.ctlSelect.controlID ) { // Level 2
          // case xxxxxx:
          //
          // handled = true;
          // break;
          // default:
          // break;
          // } // Level 2
           break;
          
           default:
           break;
           } // Level 1
          
           return handled;
          }
          
          
          /***********************************************************************
           *
           * FUNCTION: AppHandleEvent
           *
           * DESCRIPTION: This routine loads form resources and set the event
           * handler for the form loaded.
           *
           * PARAMETERS: pEvent - a pointer to an EventType structure
           *
           * RETURNED: true if the event has handle and should not be passed
           * to a higher level handler.
           *
           * REVISION HISTORY:
           *
           *
           ***********************************************************************/
     679  static Boolean AppHandleEvent(  EventType* pEvent )
          {
           UInt16 formId;
           FormType* pForm;
           Boolean handled = false;
          
           if (  pEvent->eType == frmLoadEvent ) {
           // Load the form resource.
           formId = pEvent->data.frmLoad.formID;
          
           pForm = FrmInitForm(  formId );
           FrmSetActiveForm(  pForm );
          
           // Set the event handler for the form. The handler of the currently
           // active form is called by FrmHandleEvent each time is receives an
           // event.
           switch (  formId ) {
           case MainForm:
           FrmSetEventHandler(  pForm,   MainFormHandleEvent );
           break;
          
           default:
           break;
           }
           handled = true;
           }
          
           return handled;
          }
          
          
          /***********************************************************************
           *
           * FUNCTION: AppStart
           *
           * DESCRIPTION: Get the current application's preferences.
           *
           * PARAMETERS: nothing
           *
           * RETURNED: Err value errNone if nothing went wrong
           *
           * REVISION HISTORY:
           *
           *
           ***********************************************************************/
     724  static Err AppStart(  void )
          {
           FrmGotoForm(  MainForm );
           return errNone;
          }
          
          
          /***********************************************************************
           *
           * FUNCTION: AppStop
           *
           * DESCRIPTION: Save the current state of the application.
           *
           * PARAMETERS: nothing
           *
           * RETURNED: nothing
           *
           * REVISION HISTORY:
           *
           *
           ***********************************************************************/
     745  static void AppStop(  void )
          {
           // Close all the open forms.
           FrmCloseAllForms(   );
          }
          
          
          /***********************************************************************
           *
           * FUNCTION: AppEventLoop
           *
           * DESCRIPTION: This routine is the event loop for the application.
           *
           * PARAMETERS: nothing
           *
           * RETURNED: nothing
           *
           * REVISION HISTORY:
           *
           *
           ***********************************************************************/
     766  static void AppEventLoop(  void )
          {
           Err error;
           EventType event;
          
           do {
           EvtGetEvent(  &event,   evtWaitForever );
          
           if (  SysHandleEvent(  &event ) )
           continue;
          
           if (  MenuHandleEvent(  0,   &event,   &error ) )
           continue;
          
           if (  AppHandleEvent(  &event ) )
           continue;
          
           FrmDispatchEvent(  &event );
          
           } while (  event.eType != appStopEvent );
          }
          
          /***********************************************************************
           *
           * Entry Points
           *
           ***********************************************************************/
          
          
          /***********************************************************************
           *
           * FUNCTION: PilotMain
           *
           * DESCRIPTION: This is the main entry point for the application.
           *
           * PARAMETERS: cmd - word value specifying the launch code.
           * cmdPB - pointer to a structure that is associated with the launch code.
           * launchFlags - word value providing extra information about the launch.
           * RETURNED: Result of launch
           *
           * REVISION HISTORY: 3rd major revision (  attempt ) to get Interface
           * functionality from the forms. -sra
           *
           *
           ***********************************************************************/
     811  UInt32 PilotMain(  UInt16 cmd,   MemPtr cmdPBP,   UInt16 launchFlags )
          {
           Err error = errNone;
          
           switch (  cmd ) {
           case sysAppLaunchCmdNormalLaunch:
           if (  (  error = AppStart(   ) ) == 0 ) {
           AppEventLoop(   );
           AppStop(   );
           }
           break;
          
           default:
           break;
           }
          
           return error;
          }