/* Public domain */ #ifdef AG_NAMESPACES /* * Lookup a registered namespace by name. */ #ifdef AG_INLINE_HEADER static __inline__ AG_Namespace *_Nullable AG_GetNamespace(const char *_Nonnull ns) #else AG_Namespace * ag_get_namespace(const char *ns) #endif { int i; for (i = 0; i < agNamespaceCount; i++) { if (strcmp(agNamespaceTbl[i].name, ns) == 0) return (&agNamespaceTbl[i]); } AG_SetError("No such namespace: %s", ns); return (NULL); } #endif /* AG_NAMESPACES */ /* * Compare the inheritance hierarchy of a class against a given pattern. */ #ifdef AG_INLINE_HEADER static __inline__ int AG_ClassIsNamed(const void *_Nonnull pClass, const char *_Nonnull pat) #else int ag_class_is_named(const void *pClass, const char *pat) #endif { const AG_ObjectClass *cls = (const AG_ObjectClass *)pClass; const char *c; AG_Size patSize; int nwild = 0; for (c = &pat[0]; *c != '\0'; c++) { if (*c == '*') nwild++; } if (nwild == 0) { return (strcmp(cls->hier, pat) == 0); } else if (nwild == 1) { /* Optimized case */ if (pat[strlen(pat)-1] == '*') { for (c = &pat[0]; *c != '\0'; c++) { if (c[0] != ':' || c[1] != '*' || c[2] != '\0') continue; if (c == &pat[0]) { return (1); } patSize = c - &pat[0]; if (!strncmp(cls->hier, pat, patSize) && (cls->hier[patSize] == ':' || cls->hier[patSize] == '\0')) { return (1); } } } else if (pat[0] == '*') { return (1); } else { return AG_ClassIsNamedGeneral(cls, pat); } return (0); } return AG_ClassIsNamedGeneral(cls, pat); /* General case */ } /* * Test whether an object's class matches a given pattern. */ #ifdef AG_INLINE_HEADER static __inline__ int AG_OfClass(const void *_Nonnull obj, const char *_Nonnull spec) #else int ag_of_class(const void *obj, const char *spec) #endif { return AG_ClassIsNamed(AGOBJECT(obj)->cls, spec); } /* * Return a pointer to the root of the given object's VFS. * The result is valid as long as the VFS is locked. */ #ifdef AG_INLINE_HEADER static __inline__ AG_Object *_Nonnull _Pure_Attribute AG_ObjectRoot(const void *_Nonnull obj) #else AG_Object * ag_object_root(const void *obj) #endif { return AGOBJECT(obj)->root; } /* * Return a pointer to the parent of the given object, if any. * The result is valid as long as the VFS is locked. */ #ifdef AG_INLINE_HEADER static __inline__ AG_Object *_Nullable AG_ObjectParent(const void *_Nonnull obj) #else AG_Object * ag_object_parent(const void *obj) #endif { return AGOBJECT(obj)->parent; } /* * Detach and destroy an object. */ #ifdef AG_INLINE_HEADER static __inline__ void AG_ObjectDelete(void *_Nonnull pObj) #else void ag_object_delete(void *pObj) #endif { AG_Object *obj = AGOBJECT(pObj); if (obj->parent != NULL) { AG_ObjectDetach(obj); } AG_ObjectDestroy(obj); } /* * Lookup a direct child object by name. * Result is valid as long as parent object's VFS is locked. */ #ifdef AG_INLINE_HEADER static __inline__ void *_Nullable _Pure_Attribute_If_Unthreaded AG_ObjectFindChild(void *_Nonnull pParent, const char *_Nonnull name) #else void * ag_object_find_child(void *pParent, const char *name) #endif { AG_Object *pObj = AGOBJECT(pParent); AG_Object *cObj; AG_LockVFS(pObj); AGOBJECT_FOREACH_CHILD(cObj, pObj, ag_object) { if (strcmp(cObj->name, name) == 0) break; } AG_UnlockVFS(pObj); return (cObj); } /* Return a pointer to the description of the superclass an object. */ #ifdef AG_INLINE_HEADER static __inline__ AG_ObjectClass *_Nullable _Pure_Attribute AG_ObjectSuperclass(const void *_Nonnull p) #else AG_ObjectClass *_Nullable _Pure_Attribute ag_object_superclass(const void *p) #endif { return AGOBJECT(p)->cls->super; } #ifdef AG_THREADS /* Acquire the mutex protecting all resources owned by an object. */ #ifdef AG_INLINE_HEADER static __inline__ void AG_ObjectLock(void *_Nonnull p) #else void ag_object_lock(void *p) #endif { AG_MutexLock(&AGOBJECT(p)->lock); } /* Release the mutex protecting all resources owned by an object. */ #ifdef AG_INLINE_HEADER static __inline__ void AG_ObjectUnlock(void *_Nonnull p) #else void ag_object_unlock(void *p) #endif { AG_MutexUnlock(&AGOBJECT(p)->lock); } /* Acquire the mutex protecting all resources owned by an object. */ #ifdef AG_INLINE_HEADER static __inline__ void AG_LockVFS(void *_Nonnull p) #else void ag_lock_vfs(void *p) #endif { AG_ObjectLock(AGOBJECT(p)->root); } /* Release the mutex protecting all resources owned by an object. */ #ifdef AG_INLINE_HEADER static __inline__ void AG_UnlockVFS(void *_Nonnull p) #else void ag_unlock_vfs(void *p) #endif { AG_ObjectUnlock(AGOBJECT(p)->root); } #ifdef AG_TIMERS /* Lock the timer queue (and optionally all timers owned by a given object). */ #ifdef AG_INLINE_HEADER static __inline__ void AG_LockTimers(void *_Nullable p) #else void ag_lock_timers(void *p) #endif { AG_Object *ob = (p != NULL) ? AGOBJECT(p) : &agTimerMgr; AG_ObjectLock(ob); AG_LockTiming(); } /* Unlock the timer queue (and optionally all timers owned by a given object). */ #ifdef AG_INLINE_HEADER static __inline__ void AG_UnlockTimers(void *_Nullable p) #else void ag_unlock_timers(void *p) #endif { AG_Object *ob = (p != NULL) ? AGOBJECT(p) : &agTimerMgr; AG_UnlockTiming(); AG_ObjectUnlock(ob); } #endif /* AG_TIMERS */ #endif /* AG_THREADS */ /* * Evaluate whether the named object variable exists. * The object must be locked. */ #ifdef AG_INLINE_HEADER static __inline__ int _Pure_Attribute AG_Defined(void *_Nonnull pObj, const char *_Nonnull name) #else int ag_defined(void *pObj, const char *name) #endif { AG_Object *obj = AGOBJECT(pObj); AG_Variable *V; AG_TAILQ_FOREACH(V, &obj->vars, vars) { if (strcmp(name, V->name) == 0) return (1); } return (0); } /* * If the named variable exists, return a pointer to it. * If not, allocate a new one. The Object must be locked. */ #ifdef AG_INLINE_HEADER static __inline__ AG_Variable *_Nonnull AG_FetchVariable(void *_Nonnull pObj, const char *_Nonnull name, enum ag_variable_type type) #else AG_Variable * ag_fetch_variable(void *pObj, const char *name, enum ag_variable_type type) #endif { AG_Object *obj = (AG_Object *)pObj; AG_Variable *V; AG_TAILQ_FOREACH(V, &obj->vars, vars) { if (strcmp(V->name, name) == 0) break; } if (V == NULL) { V = AG_Malloc(sizeof(AG_Variable)); AG_InitVariable(V, type, name); AG_TAILQ_INSERT_TAIL(&obj->vars, V, vars); } return (V); } /* * Mutating variant of AG_FetchVariable(). If the named variable exists, * reinitialize it as a variable of the specified type. */ #ifdef AG_INLINE_HEADER static __inline__ AG_Variable *_Nonnull AG_FetchVariableOfType(void *_Nonnull obj, const char *_Nonnull name, enum ag_variable_type type) #else AG_Variable * ag_fetch_variable_of_type(void *obj, const char *name, enum ag_variable_type type) #endif { AG_Variable *V; V = AG_FetchVariable(obj, name, type); if (V->type != type) { #ifdef AG_DEBUG AG_Debug2(obj, "Mutate \"" AGSI_YEL "%s" AGSI_RST "\" from (" AGSI_CYAN "%s" AGSI_RST ") to (" AGSI_CYAN "%s" AGSI_RST ")\n", name, agVariableTypes[V->type].name, agVariableTypes[type].name); #endif AG_FreeVariable(V); AG_InitVariable(V, type, name); } return (V); } /* * Lookup a Variable by name and return a locked handle for it. * The caller must use AG_UnlockVariable() when finished. * * Unlike AG_FetchVariable(), the AG_AccessVariable() function will * try to dereference proxy variables (variables of type P_VARIABLE). * * The object must be locked. */ #ifdef AG_INLINE_HEADER static __inline__ AG_Variable *_Nullable _Pure_Attribute_If_Unthreaded AG_AccessVariable(void *_Nonnull pObj, const char *_Nonnull name) #else AG_Variable * ag_access_variable(void *pObj, const char *name) #endif { AG_Object *obj = AGOBJECT(pObj); AG_Variable *V, *Vtgt; AG_TAILQ_FOREACH(V, &obj->vars, vars) { if (strcmp(name, V->name) == 0) break; } if (V == NULL) { return (NULL); } AG_LockVariable(V); if (V->type == AG_VARIABLE_P_VARIABLE) { #if 0 AG_Debug2(NULL, "Aliasing \"%s\" -> %s<%s>:\"%s\"\n", name, AGOBJECT(V->data.p)->name, AGOBJECT_CLASS(V->data.p)->name, V->info.varName); #endif /* * TODO limit the recursion level so that we can * detect possible circular references. */ Vtgt = AG_AccessVariable(AGOBJECT(V->data.p), V->info.varName); AG_UnlockVariable(V); return (Vtgt); } return (V); }