
/*    

   DCTIONRY.C

   Copyright (C) 1995-1996 by Autodesk, Inc.
 
   Permission to use, copy, modify, and distribute this software in 
   object code form for any purpose and without fee is hereby granted, 
   provided that the above copyright notice appears in all copies and 
   that both that copyright notice and the limited warranty and 
   restricted rights notice below appear in all supporting 
   documentation.
 
   AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.  
   AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF 
   MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.  AUTODESK, INC.
   DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE 
   UNINTERRUPTED OR ERROR FREE.
 
   Use, duplication, or disclosure by the U.S. Government is subject to 
   restrictions set forth in FAR 52.227-19 (Commercial Computer 
   Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) 
   (Rights in Technical Data and Computer Software), as applicable.
    
   DESCRIPTION: 
  
     This program demonstrates using dictionaries.  Four commands
     are defined in this program:  CREATEDICT,  LISTDICT,  CREATEXDICT,
     and LISTXDICT.
    
     The CREATEDICT command calls the function createDict which checks
     to see if the Named Objects Dictionary contains an entry associated
     with the key "TEST_DICT".  If no entry is found,  then a new
     dictionary is created and added to the Named Objects Dictionary
     using this key.  Once a dictionary associated with the key TEST_DICT"
     is found,  an Xrecord is created,  filled in with some values,  and
     added to it. 
    
     The LISTDICT command calls the function listDict which looks in
     the Named Onjects Dictionary for a dictionary associated with the
     key "TEST_DICT".  If this dictionary is found,  it is searched
     for an Xrecord object associated with the key "XRECADS".  If this
     Xrecord is found,  it's contents are printed out to the command
     line.
     
     The CREATEXDICT command calls the function createXDict() which
     allows the user to select an entity,  checks to see if the entity
     selected already has an extension dictionary,  if not it adds one
     to the entity,  then it creates an xrecord object  fills it in with
     some dummy values,  and adds it to the extension dictionary.
    
     The LISTXDICT command calls the listXrecord() function which opens
     the extension dictionary of a user selected object,  looks for the
     xrecord created by the CREATE command and then calls the printList()
     function to print out the data stored in the xrecord.
*/

/* here's what a line entity with an extension dictionary looks
like:

((-1 . <Entity name: 2bb0540>) (0 . "LINE") (5 . "28") (102 . 
"{ACAD_XDICTIONARY") (360 . <Entity name: 2bb0548>) (102 . "}") (100 . 
"AcDbEntity") (67 . 0) (8 . "0") (100 . "AcDbLine") (10 7.10996 7.56859 0.0) 
(11 11.5349 3.75213 0.0) (210 0.0 0.0 1.0))

here's what the dictionary <Entity name: 2bb0548> looks like:

((-1 . <Entity name: 2bb0548>) (0 . "DICTIONARY") (5 . "29") (100 . 
"AcDbDictionary") (3 . "XREC1") (350 . <Entity name: 2bb0550>))

here's what the Xrecord <Entity name: 2bb0550> looks like:

((-1 . <Entity name: 2bb0550>) (0 . "XRECORD") (5 . "2A") (102 . 
"{ACAD_REACTORS") (330 . <Entity name: 2bb0548>) (102 . "}") (100 . 
"AcDbXrecord") (1 . "This is a test Xrecord list") (10 1.0 2.0 0.0) (40 . 
3.14159) (50 . 3.14159) (62 . 1) (70 . 180))

*/


#include  <stdio.h>
#include  <string.h>
#include  "rxdefs.h"
#include  "adslib.h"

#define ELEMENTS(array) (sizeof(array)/sizeof((array)[0]))


/*  local function prototypes */

int      dofun         (void);
int      funcload      (void);
int      funcunload    (void);
int      createDict    (void);
int      listDict      (void);
int      createXDict   (void);
int      listXDict     (void);
int      makeDict      (ads_name name);
int      addXrecord    (ads_name name);
int      printList     (struct resbuf*);


/*  LISP registered functions list structure
      c:createdict  -- creates a dictionary,  adds it to the named objects
                       dictionary,  and adds an Xrecord to it.
      c:listdict    -- lists the xrecord in the dictionary created by
                       c:createdict.
      c:createxdict -- creates a dictionary,  adds it as the extensions
                       dictionary to an entity,  and adds an Xrecord to
                       it.
      c:listxdict   -- lists the xrecord in the extension dictionary
                       created by c:createxdict
*/
struct func_entry { char *func_name; int (*func) (); };
static struct func_entry func_table[] = {{"c:createdict", createDict},
                                         {"c:listdict", listDict},
                                         {"c:createxdict", createXDict},
                                         {"c:listxdict", listXDict}};

extern "C" AcRx::AppRetCode 
acrxEntryPoint(AcRx::AppMsgCode msg, void* ptr)
{
    switch(msg) {
	case AcRx::kInitAppMsg:
        acrxUnlockApplication(ptr);
        break;
    case AcRx::kInvkSubrMsg:
        dofun();
        break;
    case AcRx::kLoadDwgMsg:
        funcload();
        break;
    case AcRx::kUnloadDwgMsg:
        funcunload();
        ads_printf(/*MSG2*/"Unloading.\n");
    }
    return AcRx::kRetOK;
}

/* Define this application's external functions.  Return
   RTERROR on error, else RTNORM. */

static int
funcload()
{
    short i;

    for (i = 0; i < ELEMENTS(func_table); i++) {
        if (!ads_defun(func_table[i].func_name, i))
            return RTERROR;
    }
    
    return RTNORM;
}

/* Undefine this application's external functions  */

int
/*FCN*/funcunload()
{
    short i;

    /* Undefine each function we defined */

    for (i = 0; i < ELEMENTS(func_table); i++) {
        ads_undef(func_table[i].func_name,i);
    }

    return RTNORM;
}

/* Execute external function (called upon an RQSUBR request).  
   Return value from the function executed, RTNORM or RTERROR. */

static int
dofun()
{
    int val;

    /* Get the function code and check that it's within range. */
    /*   (It can't fail to be, but paranoia doesn't hurt.)     */
 
    if ((val = ads_getfuncode()) < 0 || val >= ELEMENTS(func_table)) {
        ads_fail("Received nonexistent function code.");
        return RTERROR;
    }

    /* Call the handler and return its success-failure status. */

    return (*func_table[val].func)();
}


/* Creates an xrecord,  fills it in with some data,  and adds it to
   the dictionary associated with the key "XRECADS".  If this
   dictionary doesn't exist,  it's created and added to the named
   objects dictionary.  */

int
createDict()
{
    ads_name namedObjDict, testDict;
    struct resbuf *pDict, *pTemp;

    /* get the entity name for the named object dictionary */

    ads_namedobjdict(namedObjDict);

    /* check to see if there is already a dictionary in the 
       named objects dictionary associated with the key "TEST_DICT".
       If so,  use it.  If not,  make a new dictionary and add it
       to the named objects dictionary with the key "TEST_DICT".
      
       If there is no entry with the key "TEST_DICT" then the error:
      
       Application C:\...\dctionry.exe ERROR: AutoCAD rejected function
      
       is generated on the AutoCAD command line,  but the program
       continues on and works fine. */
      
    pDict = ads_dictsearch(namedObjDict, "TEST_DICT", 0);

    if (pDict) {
        /* First check to be sure the entry is a dictionary */
        
        for (pTemp = pDict;pTemp->restype != 0;pTemp = pTemp->rbnext)
            { ; }

        /* Remember,  strcmp returns nonzero (i.e. TRUE) if strings are
           not the same. */
        
        if (strcmp(pTemp->resval.rstring, "DICTIONARY")) {
            ads_relrb(pDict);
            ads_printf("\nTEST_DICT entry exists,  but is not a dictionary!");
            ads_retvoid();
            return RTNORM;
        }
        
        /* find the dictionary's entity name resbuf.  It should be the
           first resbuf,  but no sense in taking chances. */
        
        for (pTemp = pDict;pTemp->restype != -1;pTemp = pTemp->rbnext)
            { ; }
        
        ads_relrb(pDict);

        /* Next check to see if it's already got an entry with the key
           "XRECADS".  If so,  then inform the user and return. */
        
        if (ads_dictsearch(pTemp->resval.rlname, "XRECADS", 0)) {
            ads_printf(
                "\nXRECADS entry already exists in TEST_DICT dictionary");
            ads_retvoid();
            return RTNORM;
        } else {
            /* set testDict to the dictionary's entity name */
            
            ads_name_set(pTemp->resval.rlname, testDict);
        }
    } else {
        makeDict(testDict);
        ads_dictadd(namedObjDict, "TEST_DICT", testDict);
    }

    /* At this point testDict contains a valid dictionary entity name.
       So,  add an Xrecord to the dictionary it specifies. */
    
    addXrecord(testDict);

    ads_retvoid();
    return RTNORM;
}


/* Accesses the xrecord associated with the key "XRECADS" in the
   dictionary associated the key "TEST_DICT".  Then lists out the
   contents of this xrecord using the printList function. */
  
int
listDict()
{
    struct resbuf *pDict, *pTemp, *pXrec;
    ads_name namedObjDict;

    /* get the entity name for the named object dictionary */
    
    ads_namedobjdict(namedObjDict);

    pDict = ads_dictsearch(namedObjDict, "TEST_DICT", 0);

    if (!pDict) {
        ads_printf("\nFailed to find test dictionary");
        ads_retvoid();
        return RTNORM;
    }

    /* find the dictionary's entity name resbuf.  It should be the
       first resbuf,  but no sense in taking chances. */
    
    for (pTemp = pDict;pTemp->restype != -1;pTemp = pTemp->rbnext)
        { ; }
    
    pXrec = ads_dictsearch(pTemp->resval.rlname, "XRECADS", 0);

    ads_relrb(pDict);

    if(pXrec) {
        printList(pXrec);
        ads_relrb(pXrec);
    }

    ads_retvoid();
    return RTNORM;
}


/* Allows the user to select an entity.  It then checks to see if the
   selected entity has an extension dictionary.  If not,  it creates
   one and adds it to the entity.  It then creates an xrecord,  fills it
   in with some data,  and adds it to the extension dictionary
   associated with the key "XRECADS". */

int
createXDict()
{
    struct resbuf *pEnt, *pDict, *pTemp, *pTemp2;
    ads_point dummy;
    ads_name ename, extDict = {0L, 0L};
    int status;

    /* have user select an entity.  Then get it's data */
    
    if ((status = ads_entsel("\nselect entity: ", ename, dummy)) != RTNORM) {
        return status;
    }
    pEnt = ads_entget(ename);

    /* Now check to see if the entity already has an extension
       dictionary */
    
    for (pTemp = pEnt;pTemp->rbnext != NULL;pTemp = pTemp->rbnext) {
        if (pTemp->restype == 102) {
            if (!strcmp("{ACAD_XDICTIONARY", pTemp->resval.rstring)) {
                ads_name_set(pTemp->rbnext->resval.rlname, extDict);
                break;
            }
        }
    }

    /* If no extension dictionary exists add one */
    
    if (extDict[0] == 0L) {

        /* make a new empty dictionary object using the local function
           makeDict(). */
        
        makeDict(extDict);

        /* now set it's owner to be the selected entity so that it
           becomes an extension dictionary */
          
        pDict = ads_buildlist(102, "{ACAD_XDICTIONARY", 360, extDict,
            102, "}", 0);
        for (pTemp = pEnt;pTemp->rbnext->restype != 100;pTemp = pTemp->rbnext)
            { ; }
        for (pTemp2 = pDict;pTemp2->rbnext != NULL;pTemp2 = pTemp2->rbnext)
            { ; }
        pTemp2->rbnext = pTemp->rbnext;
        pTemp->rbnext = pDict;

        ads_entmod(pEnt);
        ads_relrb(pEnt);
    } else {
        /* check to see if it's already got an entry with the key "XRECADS".
           If so,  then inform the user and return. */
        
        if (ads_dictsearch(extDict, "XRECADS", 0)) {
            ads_printf(
                "\nXRECADS entry already exists in extension dictionary");
            ads_retvoid();
            return RTNORM;
        }
    }
     
    
    /* At this point the entity has an extension dictionary.  So,
       call the local function addXrecord() to add an Xrecord to
       the dictionary. */
    
    addXrecord(extDict);

    ads_retvoid();
    return RTNORM;
}


/* Accesses the xrecord associated with the key "XRECADS" in the
   dictionary associated with the key "TEST_DICT".  Then lists out
   the contents of this xrecord using the printList function. */
  
int
listXDict()
{
    struct resbuf *pXrec, *pEnt, *pTemp;
    ads_point dummy;
    ads_name ename, extDict = {0L, 0L};
    int status;

    /* have user select an entity.  Then get it's data */
    
    if ((status = ads_entsel("\nselect entity: ", ename, dummy)) != RTNORM) {
        return status;
    }
    pEnt = ads_entget(ename);

    /* Now get the entity name of the extension dictionary */
    
    for (pTemp = pEnt;pTemp->rbnext != NULL;pTemp = pTemp->rbnext) {
        if (pTemp->restype == 102) {
            if (!strcmp("{ACAD_XDICTIONARY", pTemp->resval.rstring)) {
                ads_name_set(pTemp->rbnext->resval.rlname, extDict);
                break;
            }
        }
    }

    if (extDict[0] == 0L) {
        ads_printf("\nNo extension dictionary present.");
        ads_retvoid();
        return RTNORM;
    }

    pXrec = ads_dictsearch(extDict, "XRECADS", 0);

    if(pXrec) {
        printList(pXrec);
        ads_relrb(pXrec);
    }

    ads_retvoid();
    return RTNORM;
}


/* makes a new empty dictionary object with no owner and sets the name
   argument to it's entity name. */
  
int
makeDict(ads_name name)
{
        struct resbuf *pDict;
        
        pDict = ads_buildlist(RTDXF0, "DICTIONARY", 100,
            "AcDbDictionary", 0);
        ads_entmakex(pDict, name);
        ads_relrb(pDict);

        return RTNORM;
  }


/* creates a sample Xrecord and adds it to the dictionary specified
   by the ads_name argument passed in */

int
addXrecord(ads_name name)
{
    struct resbuf *pXrec;
    ads_point testpt = {1.0, 2.0, 0.0};
    ads_name xrecName;
    
    /* create a resbuf list of the xrecord's entity info and data */
      
    pXrec = ads_buildlist(RTDXF0, "XRECORD",
        100, "AcDbXrecord",
        1, "This is a test Xrecord list",   /* AcDb::kDxfText   */
        10, testpt,                         /* AcDb::kDxfXCoord */
        40, 3.14159,                        /* AcDb::kDxfReal   */
        50, 3.14159,                        /* AcDb::kDxfAngle  */
        60, 1,                              /* AcDb::kDxfColor  */
        70, 180,                            /* AcDb::kDxfInt16  */
        0);

    /* create the Xrecord with no owner set.  The Xrecord's new
       entity name will be placed into the xrecname argument */
    
    ads_entmakex (pXrec, xrecName);

    /* release the resbuf list now that we no longer need it */
    
    ads_relrb(pXrec);

    /* set the Xrecord's owner to be the dictionary specified by "name" */
    
    ads_dictadd(name, "XRECADS", xrecName);

    return RTNORM;
}


/* accepts a list of resbufs and prints out the values they contain */

int
printList(struct resbuf *pBuf)
{
    int rt, i;
    char buf[133];     /* should only need 1 byte for <return>, but
                          play it safe since ads_getstring can accept
                          up to 132 characters */

    ads_printf("\n");  /* to make sure we start on a new line */
    
    for (i = 0;pBuf != NULL;i++, pBuf = pBuf->rbnext)
    {
        if (pBuf->restype < 0)
            rt = pBuf->restype;         /* Entity name (or other sentinel) */
        else if (pBuf->restype < 10)
            rt = RTSTR;
        else if (pBuf->restype < 38)
            rt = RT3DPOINT;
        else if (pBuf->restype < 60)
            rt = RTREAL;
        else if (pBuf->restype < 80)
            rt = RTSHORT;
        else if (pBuf->restype < 100)
            rt = RTLONG;
        else if (pBuf->restype < 106)
            rt = RTSTR;
        else if (pBuf->restype < 148)
            rt = RTREAL;
        else if (pBuf->restype < 290)
            rt = RTSHORT;
        else if (pBuf->restype < 320)
            rt = RTSTR;
        else if (pBuf->restype < 370)
            rt = RTENAME;
        else if (pBuf->restype < 999)
            rt = RT3DPOINT;
        else                      /* pBuf->restype is already RTSHORT,  */
            rt = pBuf->restype;   /* RTSTR, etc. or else it is unknown. */

        switch (rt) {
        case RTSHORT:
            if (pBuf->restype == RTSHORT)
                ads_printf("RTSHORT : %d\n", pBuf->resval.rint);
            else
                ads_printf("(%d . %d)\n", pBuf->restype, pBuf->resval.rint);
            break;
        case RTREAL:
            if (pBuf->restype == RTREAL)
                ads_printf("RTREAL : %0.3f\n", pBuf->resval.rreal);
            else
                ads_printf("(%d . %0.3f)\n", pBuf->restype
                    , pBuf->resval.rreal);
            break;
        case RTSTR:
            if (pBuf->restype == RTSTR)
                ads_printf("RTSTR : %s\n", pBuf->resval.rstring);
            else
                ads_printf("(%d . \"%s\")\n", pBuf->restype
                    , pBuf->resval.rstring);
            break;
        case RT3DPOINT:
            if (pBuf->restype == RT3DPOINT)
                ads_printf("RT3DPOINT : %0.3f, %0.3f, %0.3f\n",
                    pBuf->resval.rpoint[X],  pBuf->resval.rpoint[Y]
                    , pBuf->resval.rpoint[Z]);
            else
                ads_printf("(%d %0.3f %0.3f %0.3f)\n", pBuf->restype,
                    pBuf->resval.rpoint[X], pBuf->resval.rpoint[Y],
                    pBuf->resval.rpoint[Z]);
            break;
        case RTLONG:
            ads_printf("RTLONG : %dl\n", pBuf->resval.rlong);
            break;
        case -1:
        case RTENAME:                          /* First block entity */
            ads_printf("(%d . <Entity name: %8lx>)\n", pBuf->restype,
                pBuf->resval.rlname[0]);
            break;
        case -3:                              /* marks start of xdata */
            ads_printf("(-3)\n");
        }

        if ((i == 23) && (pBuf->rbnext != NULL)) {
            i = 0;
            ads_getstring(0,"Press <ENTER> to continue...", buf);
        }
    }
    return RTNORM;
}
