/* * Copyright (c) 2003-2008 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Functions related to Agar object classes and namespaces. */ #include #include #include extern AG_ObjectClass agObjectClass; AG_ObjectClass *agClassTree = NULL; /* Root */ AG_Namespace *agNamespaceTbl = NULL; int agNamespaceCount = 0; char **agModuleDirs = NULL; int agModuleDirCount = 0; AG_Mutex agClassLock; static void InitClass(AG_ObjectClass *cl, const char *hier, const char *libs) { const char *c; Strlcpy(cl->hier, hier, sizeof(cl->hier)); Strlcpy(cl->libs, libs, sizeof(cl->libs)); if ((c = strrchr(hier, ':')) != NULL && c[1] != '\0') { Strlcpy(cl->name, &c[1], sizeof(cl->name)); } else { Strlcpy(cl->name, hier, sizeof(cl->name)); } TAILQ_INIT(&cl->sub); } /* Initialize the object class description table. */ void AG_InitClassTbl(void) { agNamespaceTbl = Malloc(sizeof(AG_Namespace)); agNamespaceCount = 0; agModuleDirs = Malloc(sizeof(char *)); agModuleDirCount = 0; AG_RegisterNamespace("Agar", "AG_", "http://libagar.org/"); InitClass(&agObjectClass, "AG_Object", ""); agClassTree = &agObjectClass; AG_MutexInitRecursive(&agClassLock); } /* Release the object class description table. */ void AG_DestroyClassTbl(void) { agClassTree = NULL; Free(agNamespaceTbl); agNamespaceTbl = NULL; agNamespaceCount = 0; AG_MutexDestroy(&agClassLock); } /* Convert a class specification in "Namespace1(Class1:Class2)[@lib]" format. */ int AG_ParseClassSpec(AG_ObjectClassSpec *cs, const char *spec) { char buf[AG_OBJECT_HIER_MAX], *pBuf, *pTok; char nsName[AG_OBJECT_HIER_MAX], *pNsName = nsName; const char *s, *p, *pOpen = NULL; char *c; int iTok, i = 0; AG_Namespace *ns; if (strlen(spec) >= AG_OBJECT_HIER_MAX) { AG_SetError("Hierarchy string too long"); return (-1); } /* Parse the inheritance hierarchy and library list. */ cs->hier[0] = '\0'; cs->libs[0] = '\0'; cs->name[0] = '\0'; *pNsName = '\0'; for (s = &spec[0]; *s != '\0'; s++) { if (s[0] == '(' && s[1] != '\0') { if (pOpen || nsName[0] == '\0') { AG_SetError("Syntax error"); return (-1); } pOpen = &s[1]; continue; } if (pOpen == NULL) { if (*s != ':') { *pNsName = *s; pNsName++; } if (s[0] == '@' && s[1] != '\0') Strlcpy(cs->libs, &s[1], sizeof(cs->libs)); } if (*s == ')') { if ((s - pOpen) == 0) { pOpen = NULL; continue; } *pNsName = '\0'; pNsName = &nsName[0]; if ((ns = AG_GetNamespace(nsName)) == NULL) { AG_SetError("No such namespace: \"%s\"", nsName); return (-1); } for (p = pOpen, iTok = 0; (p < s) && (iTok < sizeof(buf)-1); p++, iTok++) { buf[iTok] = *p; } buf[iTok] = '\0'; for (pBuf = buf; (pTok = Strsep(&pBuf, ":")) != NULL; ) { i += Strlcpy(&cs->hier[i], ns->pfx, sizeof(cs->hier)-i); i += Strlcpy(&cs->hier[i], pTok, sizeof(cs->hier)-i); i += Strlcpy(&cs->hier[i], ":", sizeof(cs->hier)-i); } pOpen = NULL; continue; } } if (i > 0 && cs->hier[i-1] == ':') { cs->hier[i-1] = '\0'; /* Strip last ':' */ } if (i == 0) { /* Flat format */ Strlcpy(cs->hier, spec, sizeof(cs->hier)); } else { cs->hier[i] = '\0'; } /* Fill in the "name" field. */ if ((c = strrchr(cs->hier, ':')) != NULL && c[1] != '\0') { Strlcpy(cs->name, &c[1], sizeof(cs->name)); } else { Strlcpy(cs->name, cs->hier, sizeof(cs->name)); } /* Fill in the "spec" field. */ Strlcpy(cs->spec, cs->hier, sizeof(cs->spec)); if (cs->libs[0] != '\0') { Strlcat(cs->spec, cs->libs, sizeof(cs->spec)); } return (0); } /* Register object class as described by the given AG_ObjectClass structure. */ void AG_RegisterClass(void *p) { AG_ObjectClass *cl = p; AG_ObjectClassSpec cs; char *s; /* Parse the class specification. */ if (AG_ParseClassSpec(&cs, cl->hier) == -1) { AG_FatalError(NULL); } InitClass(cl, cs.hier, cs.libs); #ifdef AG_CLASSDEBUG Debug(NULL, "Registered class: %s: %s (%s)\n", cs.name, cs.hier, cs.libs); #endif /* Lookup the superclass. */ AG_MutexLock(&agClassLock); if ((s = strrchr(cs.hier, ':')) != NULL) { *s = '\0'; if ((cl->super = AG_LookupClass(cs.hier)) == NULL) AG_FatalError("%s: No such superclass", cs.hier); } else { cl->super = agClassTree; /* Root */ } TAILQ_INSERT_TAIL(&cl->super->sub, cl, subclasses); AG_MutexUnlock(&agClassLock); } /* Unregister an object class. */ void AG_UnregisterClass(void *p) { AG_ObjectClass *cl = p; AG_ObjectClass *clSuper = cl->super; AG_MutexLock(&agClassLock); #ifdef AG_CLASSDEBUG Debug(NULL, "Unregistering class: %s\n", cl->name); #endif TAILQ_REMOVE(&clSuper->sub, cl, subclasses); cl->super = NULL; AG_MutexUnlock(&agClassLock); } /* XXX ridiculous */ static AG_ObjectClass * FindClassByName(AG_ObjectClass *cl, const char *spec) { AG_ObjectClass *clSub, *rv; TAILQ_FOREACH(clSub, &cl->sub, subclasses) { if (strcmp(clSub->hier, spec) == 0) { return (clSub); } if ((rv = FindClassByName(clSub, spec)) != NULL) return (rv); } return (NULL); } /* * Return information about the currently registered class matching the * given specification. */ AG_ObjectClass * AG_LookupClass(const char *inSpec) { AG_ObjectClassSpec cs; AG_ObjectClass *cl; if (inSpec[0] == '\0' || strcmp(inSpec, "Agar(Object)") == 0 || strcmp(inSpec, "AG_Object") == 0) return (agClassTree); /* Root */ if (AG_ParseClassSpec(&cs, inSpec) == -1) AG_FatalError(NULL); /* Recursively search the class structure. */ AG_MutexLock(&agClassLock); if ((cl = FindClassByName(agClassTree, cs.hier)) != NULL) { AG_MutexUnlock(&agClassLock); return (cl); } AG_MutexUnlock(&agClassLock); AG_SetError("No such class: %s", inSpec); return (NULL); } /* * Look for a "@libs" string in the class specification and scan module * directories for the required libraries. If they are found, bring them * into the current process's address space. If successful, look up the * "pfxFooClass" symbol and register the class. * * Multiple libraries can be specified with commas. The "pfxFooClass" * symbol is assumed to be defined in the first library in the list. */ AG_ObjectClass * AG_LoadClass(const char *classSpec) { AG_ObjectClassSpec cs; char *s, *lib; char sym[AG_OBJECT_HIER_MAX]; AG_DSO *dso; void *pClass = NULL; int i; if (AG_ParseClassSpec(&cs, classSpec) == -1) AG_FatalError(NULL); if (cs.libs[0] == '\0') return AG_LookupClass(cs.hier); AG_MutexLock(&agClassLock); /* Load all libraries specified in the string. */ for (i = 0, s = cs.libs; (lib = Strsep(&s, ", ")) != NULL; i++) { if ((dso = AG_LoadDSO(lib, 0)) == NULL) { AG_SetError("Loading <%s>: %s", classSpec, AG_GetError()); goto fail; } /* Look up "pfxFooClass" in the first library. */ if (i == 0) { Strlcpy(sym, cs.name, sizeof(sym)); Strlcat(sym, "Class", sizeof(sym)); if (AG_SymDSO(dso, sym, &pClass) == -1) { AG_SetError("Loading <%s>: %s", lib, AG_GetError()); AG_UnloadDSO(dso); /* XXX TODO undo other DSOs we just loaded */ goto fail; } } } if (pClass == NULL) { AG_SetError("Loading <%s>: No library specified", classSpec); goto fail; } AG_RegisterClass(pClass); AG_MutexUnlock(&agClassLock); return (pClass); fail: AG_MutexUnlock(&agClassLock); return (pClass); } /* * Unregister the given class and decrement the reference count / unload * related dynamically-linked libraries. */ void AG_UnloadClass(AG_ObjectClass *cl) { char *s, *lib; AG_DSO *dso; AG_UnregisterClass(cl); for (s = cl->libs; (lib = Strsep(&s, ", ")) != NULL; ) { if ((dso = AG_LookupDSO(lib)) != NULL) AG_UnloadDSO(dso); } } /* Register a new namespace. */ AG_Namespace * AG_RegisterNamespace(const char *name, const char *pfx, const char *url) { AG_Namespace *ns; agNamespaceTbl = Realloc(agNamespaceTbl, (agNamespaceCount+1)*sizeof(AG_Namespace)); ns = &agNamespaceTbl[agNamespaceCount++]; ns->name = name; ns->pfx = pfx; ns->url = url; return (ns); } /* Unregister a namespace. */ void AG_UnregisterNamespace(const char *name) { int i; for (i = 0; i < agNamespaceCount; i++) { if (strcmp(agNamespaceTbl[i].name, name) == 0) break; } if (i == agNamespaceCount) { return; } if (i < agNamespaceCount-1) { memmove(&agNamespaceTbl[i], &agNamespaceTbl[i+1], (agNamespaceCount-1)*sizeof(AG_Namespace)); } agNamespaceCount--; } /* Register a new module directory path. */ void AG_RegisterModuleDirectory(const char *path) { char *s, *p; agModuleDirs = Realloc(agModuleDirs, (agModuleDirCount+1)*sizeof(char *)); agModuleDirs[agModuleDirCount++] = s = Strdup(path); if (*(p = &s[strlen(s)-1]) == AG_PATHSEPCHAR) *p = '\0'; } /* Unregister a module directory path. */ void AG_UnregisterModuleDirectory(const char *path) { int i; for (i = 0; i < agModuleDirCount; i++) { if (strcmp(agModuleDirs[i], path) == 0) break; } if (i == agModuleDirCount) { return; } free(agModuleDirs[i]); if (i < agModuleDirCount-1) { memmove(&agModuleDirs[i], &agModuleDirs[i+1], (agModuleDirCount-1)*sizeof(char *)); } agModuleDirCount--; } /* General case fallback for AG_ClassIsNamed() */ int AG_ClassIsNamedGeneral(const AG_ObjectClass *cl, const char *cn) { char cname[AG_OBJECT_HIER_MAX], *cp, *c; char nname[AG_OBJECT_HIER_MAX], *np, *s; Strlcpy(cname, cn, sizeof(cname)); Strlcpy(nname, cl->hier, sizeof(nname)); cp = cname; np = nname; while ((c = Strsep(&cp, ":")) != NULL && (s = Strsep(&np, ":")) != NULL) { if (c[0] == '*' && c[1] == '\0') continue; if (strcmp(c, s) != 0) return (0); } return (1); } /* * Return an array of class structures describing the inheritance * hierarchy of an object. * XXX */ int AG_ObjectGetInheritHier(void *obj, AG_ObjectClass ***hier, int *nHier) { char cname[AG_OBJECT_HIER_MAX], *c; AG_ObjectClass *cl; int i, stop = 0; if (AGOBJECT(obj)->cls->hier[0] == '\0') { (*nHier) = 0; return (0); } (*nHier) = 1; Strlcpy(cname, AGOBJECT(obj)->cls->hier, sizeof(cname)); for (c = &cname[0]; *c != '\0'; c++) { if (*c == ':') (*nHier)++; } *hier = Malloc((*nHier)*sizeof(AG_ObjectClass *)); i = 0; for (c = &cname[0];; c++) { if (*c != ':' && *c != '\0') { continue; } if (*c == '\0') { stop++; } else { *c = '\0'; } if ((cl = AG_LookupClass(cname)) == NULL) { Free(*hier); return (-1); } *c = ':'; (*hier)[i++] = cl; if (stop) break; } return (0); }