MPlugin * MPluginList::plugin_addload(plid_t plid, const char *fname, PLUG_LOADTIME now) { MPlugin pl_temp; MPlugin *pl_found, *pl_added, *pl_loader; if ( !(pl_loader=find(plid)) ) { META_DEBUG(1, ("Couldn't find plugin that gave this loading request!")); RETURN_ERRNO(NULL, ME_BADREQ); } memset(&pl_temp, 0, sizeof(pl_temp)); if (!pl_temp.plugin_parseline(fname, pl_loader->index)) { RETURN_ERRNO(NULL, ME_NOTFOUND); } if (pl_temp.resolve() != mTRUE) { META_DEBUG(1, ("Couldn't resolve given path into a file: %s", pl_temp.file)); RETURN_ERRNO(NULL, ME_NOTFOUND); } if ( (pl_found=find(pl_temp.pathname)) ) { META_DEBUG(1, ("Plugin '%s' already in current list; file=%s desc='%s'", pl_temp.file, pl_found->file, pl_found->desc)); RETURN_ERRNO(NULL, ME_ALREADY); } if (!(pl_added=add(&pl_temp))) { META_DEBUG(1, ("Couldn't add plugin '%s' to list; see log", pl_temp.desc)); return NULL; } pl_added->action = PA_LOAD; if (!pl_added->load(now)) { if (meta_errno==ME_NOTALLOWED || meta_errno==ME_DELAYED) { META_DEBUG(1, ("Plugin '%s' couldn't attach; only allowed %s", pl_added->desc, pl_added->str_loadable(SL_ALLOWED))); pl_added->clear(); } else if (pl_added->status == PL_OPENED) { META_DEBUG(1, ("Opened plugin '%s', but failed to attach; see log", pl_added->desc)); } else { META_DEBUG(1, ("Couldn't load plugin '%s'; see log", pl_added->desc)); } return NULL; } META_DEBUG(1, ("Loaded plugin '%s' successfully", pl_added->desc)); meta_errno = ME_NOERROR; return pl_added; }
// Allow plugins to call the entity functions in the GameDLL. In // particular, calling "player()" as needed by most Bots. Suggested by // Jussi Kivilinna. qboolean mutil_CallGameEntity(plid_t plid, const char *entStr, entvars_t *pev) { plugin_info_t *plinfo; ENTITY_FN pfnEntity; plinfo=(plugin_info_t *)plid; META_DEBUG(8, ("Looking up game entity '%s' for plugin '%s'", entStr, plinfo->name)); pfnEntity = (ENTITY_FN) DLSYM(GameDLL.handle, entStr); if(!pfnEntity) { META_ERROR("Couldn't find game entity '%s' in game DLL '%s' for plugin '%s'", entStr, GameDLL.name, plinfo->name); return(false); } META_DEBUG(7, ("Calling game entity '%s' for plugin '%s'", entStr, plinfo->name)); (*pfnEntity)(pev); return(true); }
MPlugin *MPluginList::find(const char *findpath) { int i; if(!findpath) RETURN_ERRNO(NULL, ME_ARGUMENT); META_DEBUG(8, ("Looking for loaded plugin with dlfnamepath: %s", findpath)); for(i=0; i < endlist; i++) { META_DEBUG(9, ("Looking at: plugin %s loadedpath: %s", plist[i].file, plist[i].pathname)); if(plist[i].status < PL_VALID) continue; if(strmatch(plist[i].pathname, findpath)) { META_DEBUG(8, ("Found loaded plugin %s", plist[i].file)); return(&plist[i]); } } META_DEBUG(8, ("No loaded plugin found with path: %s", findpath)); RETURN_ERRNO(NULL, ME_NOTFOUND); }
void do_link_ent(ENTITY_FN *pfnEntity, int *missing, char *entStr, entvars_t *pev) {; if(*missing) { META_DEBUG(9, ("Skipping entity '%s'; was previously found missing", entStr)); return; } if(!*pfnEntity) { META_DEBUG(9, ("Looking up game entity '%s'", entStr)); *pfnEntity = (ENTITY_FN) DLSYM(GameDLL.handle, entStr); } if(!*pfnEntity) { META_ERROR("Couldn't find game entity '%s' in game DLL '%s': %s", entStr, GameDLL.name, DLERROR()); *missing=1; return; } META_DEBUG(8, ("Linking game entity '%s'", entStr)); (*pfnEntity)(pev); }
mBOOL DLLINTERNAL MConfig::load(const char *fn) { FILE *fp; char loadfile[PATH_MAX]; char line[MAX_CONF_LEN]; char *optname, *optval; option_t *optp; int ln; // Make full pathname (from gamedir if relative, collapse "..", // backslashes, etc). full_gamedir_path(fn, loadfile); fp=fopen(loadfile, "r"); if(!fp) { META_WARNING("unable to open config file '%s': %s", loadfile, strerror(errno)); RETURN_ERRNO(mFALSE, ME_NOFILE); } META_DEBUG(2, ("Loading from config file: %s", loadfile)); for(ln=1; !feof(fp) && fgets(line, sizeof(line), fp); ln++) { if(line[0]=='#') continue; if(line[0]==';') continue; if(strnmatch(line, "//", 2)) continue; if(!(optname=strtok(line, " \t\r\n"))) { META_WARNING("'%s' line %d: bad config format: missing option", loadfile, ln); continue; } if(!(optval=strtok(NULL, "\r\n"))) { META_WARNING("'%s' line %d: bad config format: missing value", loadfile, ln); continue; } if(!(optp=find(optname))) { META_WARNING("'%s' line %d: unknown option name '%s'", loadfile, ln, optname); continue; } if(!set(optp, optval)) { META_WARNING("'%s' line %d: unable to set option '%s' value '%s'", loadfile, ln, optname, optval); continue; } } filename=strdup(loadfile); fclose(fp); return(mTRUE); }
MPlugin *MPluginList::find_memloc(void *memptr) { const char *dlfile; if(!memptr) RETURN_ERRNO(NULL, ME_ARGUMENT); if(!(dlfile=DLFNAME(memptr))) { META_DEBUG(8, ("DLFNAME failed to find memloc %d", memptr)); // meta_errno should be already set in DLFNAME return(NULL); } return(find(dlfile)); }
int valid_gamedir_file(char *path) { char buf[PATH_MAX]; struct stat st; int ret, reg, size; if(!path) return(FALSE); if(strmatch(path, "/dev/null")) return(TRUE); if(is_absolute_path(path)) STRNCPY(buf, path, sizeof(buf)); else snprintf(buf, sizeof(buf), "%s/%s", GameDLL.gamedir, path); ret=stat(buf, &st); if(ret != 0) { META_DEBUG(5, ("Unable to stat '%s': %s", buf, strerror(errno))); return(FALSE); } reg=S_ISREG(st.st_mode); if(!reg) { META_DEBUG(5, ("Not a regular file: %s", buf)); return(FALSE); } size=st.st_size; if(!size) { META_DEBUG(5, ("Empty file: %s", buf)); return(FALSE); } if(ret==0 && reg && size) return(TRUE); else return(FALSE); }
// Find a usermsg, registered by the gamedll, with the corresponding // msgname, and return remaining info about it (msgid, size). int mutil_GetUserMsgID(plid_t plid, const char *msgname, int *size) { plugin_info_t *plinfo; MRegMsg *umsg; plinfo=(plugin_info_t *)plid; META_DEBUG(8, ("Looking up usermsg name '%s' for plugin '%s'", msgname, plinfo->name)); umsg=RegMsgs->find(msgname); if(umsg) { if(size) *size=umsg->size; return(umsg->msgid); } else return(0); }
// Add the given cvar name to the list and return the instance. This only // writes the "name" to the new cvar; other fields are written with // cvar::set(). // meta_errno values: // - ME_NOMEM couldn't alloc or realloc for various parts MRegCvar * DLLINTERNAL MRegCvarList::add(const char *addname) { MRegCvar *icvar; if(endlist==size) { // grow array MRegCvar *temp; int i, newsize; newsize=size+REG_CVAR_GROWSIZE; META_DEBUG(6, ("Growing reg cvar list from %d to %d", size, newsize)); temp = (MRegCvar *) realloc(vlist, newsize*sizeof(MRegCvar)); if(!temp) { META_WARNING("Couldn't grow registered cvar list to %d for '%s'; %s", newsize, addname, strerror(errno)); RETURN_ERRNO(NULL, ME_NOMEM); } vlist=temp; size=newsize; // initialize new (unused) entries for(i=endlist; i<size; i++) { memset(&vlist[i], 0, sizeof(vlist[i])); vlist[i].init(i+1); // 1-based } } icvar = &vlist[endlist]; // Malloc space for the cvar and cvar name, for two reasons: // - Can't point to memory loc in plugin (another segv waiting to // happen). // - Can't point to memory in vlist which might get moved later by // realloc (again, segv). icvar->data = (cvar_t *) calloc(1, sizeof(cvar_t)); if(!icvar->data) { META_WARNING("Couldn't malloc cvar for adding reg cvar name '%s': %s", addname, strerror(errno)); RETURN_ERRNO(NULL, ME_NOMEM); } icvar->data->name=strdup(addname); if(!icvar->data->name) { META_WARNING("Couldn't strdup for adding reg cvar name '%s': %s", addname, strerror(errno)); RETURN_ERRNO(NULL, ME_NOMEM); } endlist++; return(icvar); }
// able to store a string of PATH_MAX length. char *full_gamedir_path(const char *path, char *fullpath) { char buf[PATH_MAX]; // Build pathname from filename, plus gamedir if relative path. if(is_absolute_path(path)) STRNCPY(buf, path, sizeof(buf)); else snprintf(buf, sizeof(buf), "%s/%s", GameDLL.gamedir, path); // Remove relative path components, if possible. if(!realpath(buf, fullpath)) { META_DEBUG(4, ("Unable to get realpath for '%s': %s", buf, str_os_error())); STRNCPY(fullpath, path, PATH_MAX); } // Replace backslashes, etc. normalize_pathname(fullpath); return(fullpath); }
// Find a usermsg, registered by the gamedll, with the corresponding // msgid, and return remaining info about it (msgname, size). const char *mutil_GetUserMsgName(plid_t plid, int msgid, int *size) { plugin_info_t *plinfo; MRegMsg *umsg; plinfo=(plugin_info_t *)plid; META_DEBUG(8, ("Looking up usermsg id '%d' for plugin '%s'", msgid, plinfo->name)); // Guess names for any built-in Engine messages mentioned in the SDK; // from dlls/util.h. if(msgid < 64) { switch(msgid) { case SVC_TEMPENTITY: if(size) *size=-1; return("tempentity?"); case SVC_INTERMISSION: if(size) *size=-1; return("intermission?"); case SVC_CDTRACK: if(size) *size=-1; return("cdtrack?"); case SVC_WEAPONANIM: if(size) *size=-1; return("weaponanim?"); case SVC_ROOMTYPE: if(size) *size=-1; return("roomtype?"); case SVC_DIRECTOR: if(size) *size=-1; return("director?"); } } umsg=RegMsgs->find(msgid); if(umsg) { if(size) *size=umsg->size; // 'name' is assumed to be a constant string, allocated in the // gamedll. return(umsg->name); } else return(NULL); }
// Try to call the function. Relies on OS-specific routine to attempt // calling the function without generating a segfault from an unloaded // plugin DLL. // meta_errno values: // - ME_BADREQ function disabled/invalid // - ME_ARGUMENT function pointer is null mBOOL DLLINTERNAL MRegCmd::call(void) { mBOOL ret; // can we expect to call this function? if(status != RG_VALID) RETURN_ERRNO(mFALSE, ME_BADREQ); if(!pfnCmd) RETURN_ERRNO(mFALSE, ME_ARGUMENT); // try to call this function ret=os_safe_call(pfnCmd); if(!ret) { META_DEBUG(4, ("Plugin reg_cmd '%s' called after unloaded; removed from list", name)); status=RG_INVALID; pfnCmd=NULL; // NOTE: we can't free the malloc'd space for the name, as that // would just re-introduce the segfault problem.. } // meta_errno (if failed) is set already in os_safe_call() return(ret); }
// Add the given name to the list and return the instance. This only // writes the "name" to the new cmd; other fields are writtin by caller // (meta_AddServerCommand). // meta_errno values: // - ME_NOMEM couldn't realloc or malloc for various parts MRegCmd * DLLINTERNAL MRegCmdList::add(const char *addname) { MRegCmd *icmd; if(endlist==size) { // grow array MRegCmd *temp; int i, newsize; newsize=size+REG_CMD_GROWSIZE; META_DEBUG(6, ("Growing reg cmd list from %d to %d", size, newsize)); temp = (MRegCmd *) realloc(mlist, newsize*sizeof(MRegCmd)); if(!temp) { META_WARNING("Couldn't grow registered command list to %d for '%s': %s", newsize, addname, strerror(errno)); RETURN_ERRNO(NULL, ME_NOMEM); } mlist=temp; size=newsize; // initialize new (unused) entries for(i=endlist; i<size; i++) { memset(&mlist[i], 0, sizeof(mlist[i])); mlist[i].init(i+1); // 1-based } } icmd = &mlist[endlist]; // Malloc space separately for the command name, because: // - Can't point to memory loc in plugin (another segv waiting to // happen). // - Can't point to memory in mlist which might get moved later by // realloc (again, segv). icmd->name=strdup(addname); if(!icmd->name) { META_WARNING("Couldn't strdup for adding reg cmd name '%s': %s", addname, strerror(errno)); RETURN_ERRNO(NULL, ME_NOMEM); } endlist++; return(icmd); }
mBOOL DLLINTERNAL is_gamedll(const char *filename) { HANDLE hFile; HANDLE hMap; void * mapview; IMAGE_NT_HEADERS * ntheaders; IMAGE_SECTION_HEADER * sections; int num_sects; IMAGE_EXPORT_DIRECTORY * exports; int has_GiveFnptrsToDll = 0; int has_GetEntityAPI2 = 0; int has_GetEntityAPI = 0; // Try open file for read hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(is_invalid_handle(hFile)) { META_DEBUG(3, ("is_gamedll(%s): CreateFile() failed.", filename)); return(mFALSE); } // hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if(is_invalid_handle(hMap)) { META_DEBUG(3, ("is_gamedll(%s): CreateFileMapping() failed.", filename)); CloseHandle(hFile); return(mFALSE); } // mapview = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); if(!mapview) { META_DEBUG(3, ("is_gamedll(%s): MapViewOfFile() failed.", filename)); CloseHandle(hMap); CloseHandle(hFile); return(mFALSE); } ntheaders = get_ntheaders(mapview); if(!ntheaders) { META_DEBUG(3, ("is_gamedll(%s): get_ntheaders() failed.", filename)); UnmapViewOfFile(mapview); CloseHandle(hMap); CloseHandle(hFile); return(mFALSE); } //Sections for va_to_mapaddr function sections = IMAGE_FIRST_SECTION(ntheaders); num_sects = ntheaders->FileHeader.NumberOfSections; if(IsBadReadPtr(sections, num_sects * sizeof(IMAGE_SECTION_HEADER))) { META_DEBUG(3, ("is_gamedll(%s): IMAGE_FIRST_SECTION() failed.", filename)); UnmapViewOfFile(mapview); CloseHandle(hMap); CloseHandle(hFile); return(mFALSE); } // exports = get_export_table(mapview, ntheaders, sections, num_sects); if(!exports) { META_DEBUG(3, ("is_gamedll(%s): get_export_table() failed.", filename)); UnmapViewOfFile(mapview); CloseHandle(hMap); CloseHandle(hFile); return(mFALSE); } // unsigned long * names = (unsigned long *)va_to_mapaddr(mapview, sections, num_sects, exports->AddressOfNames); if(IsBadReadPtr(names, exports->NumberOfNames * sizeof(unsigned long))) { META_DEBUG(3, ("is_gamedll(%s): Pointer to exported function names is invalid.", filename)); UnmapViewOfFile(mapview); CloseHandle(hMap); CloseHandle(hFile); return(mFALSE); } for(unsigned int i = 0; i < exports->NumberOfNames; i++) { //get function name with valid address char * funcname = (char *)va_to_mapaddr(mapview, sections, num_sects, names[i]); if(IsBadStringPtrA(funcname, 128)) continue; // Check // Fast check for 'G' first if(funcname[0] == 'G') { // Collect export information if(!has_GiveFnptrsToDll) has_GiveFnptrsToDll = strmatch(funcname, "GiveFnptrsToDll"); if(!has_GetEntityAPI2) has_GetEntityAPI2 = strmatch(funcname, "GetEntityAPI2"); if(!has_GetEntityAPI) has_GetEntityAPI = strmatch(funcname, "GetEntityAPI"); } // Check if metamod plugin else if(funcname[0] == 'M') { if(strmatch(funcname, "Meta_Init") || strmatch(funcname, "Meta_Query") || strmatch(funcname, "Meta_Attach") || strmatch(funcname, "Meta_Detach")) { // Metamod plugin.. is not gamedll META_DEBUG(5, ("is_gamedll(%s): Detected Metamod plugin, library exports [%s].", filename, funcname)); UnmapViewOfFile(mapview); CloseHandle(hMap); CloseHandle(hFile); return(mFALSE); } } } UnmapViewOfFile(mapview); CloseHandle(hMap); CloseHandle(hFile); // Check if gamedll if(has_GiveFnptrsToDll && (has_GetEntityAPI2 || has_GetEntityAPI)) { // This is gamedll! META_DEBUG(5, ("is_gamedll(%s): Detected GameDLL.", filename)); return(mTRUE); } else { META_DEBUG(5, ("is_gamedll(%s): Library isn't GameDLL.", filename)); return(mFALSE); } }
mBOOL MPluginList::refresh(PLUG_LOADTIME now) { int i, ndone=0, nkept=0, nloaded=0, nunloaded=0, nreloaded=0, ndelayed=0; MPlugin *iplug; if (!ini_refresh()) { META_ERROR("dll: Problem reloading plugins.ini: %s", inifile); // meta_errno should be already set in ini_refresh() return(mFALSE); } META_LOG("dll: Updating plugins..."); for(i=0; i < endlist; i++) { iplug=&plist[i]; if(iplug->status < PL_VALID) continue; switch(iplug->action) { case PA_KEEP: META_DEBUG(1, ("Keeping plugin '%s'", iplug->desc)); iplug->action=PA_NONE; nkept++; break; case PA_LOAD: META_DEBUG(1, ("Loading plugin '%s'", iplug->desc)); if(iplug->load(now)) nloaded++; else if(meta_errno==ME_DELAYED) ndelayed++; break; case PA_RELOAD: META_DEBUG(1, ("Reloading plugin '%s'", iplug->desc)); if(iplug->reload(now, PNL_FILE_NEWER)) nreloaded++; else if(meta_errno==ME_DELAYED) ndelayed++; break; case PA_NONE: // If previously loaded from ini, but apparently removed from new ini. if(iplug->source==PS_INI && iplug->status >= PL_RUNNING) { META_DEBUG(1, ("Unloading plugin '%s'", iplug->desc)); iplug->action=PA_UNLOAD; if(iplug->unload(now, PNL_INI_DELETED, PNL_INI_DELETED)) nunloaded++; else if(meta_errno==ME_DELAYED) ndelayed++; } break; case PA_ATTACH: // Previously requested attach, but was delayed? META_DEBUG(1, ("Retrying attach plugin '%s'", iplug->desc)); if(iplug->retry(now, PNL_DELAYED)) nloaded++; else if(meta_errno==ME_DELAYED) ndelayed++; break; case PA_UNLOAD: // Previously requested unload, but was delayed? META_DEBUG(1, ("Retrying unload plugin '%s'", iplug->desc)); if(iplug->retry(now, PNL_DELAYED)) nunloaded++; else if(meta_errno==ME_DELAYED) ndelayed++; break; case PA_NULL: META_ERROR("dll: Unexpected action for plugin '%s': '%s'", iplug->desc, iplug->str_action()); break; default: META_ERROR("dll: Unrecognized action for plugin '%s': '%s'", iplug->desc, iplug->str_action()); break; } ndone++; } META_LOG("dll: Finished updating %d plugins; kept %d, loaded %d, unloaded %d, reloaded %d, delayed %d", ndone, nkept, nloaded, nunloaded, nreloaded, ndelayed); return(mTRUE); }
mBOOL MPluginList::ini_refresh() { FILE *fp; char line[MAX_STRBUF_LEN]; int n, ln; MPlugin pl_temp; MPlugin *pl_found, *pl_added; fp=fopen(inifile, "r"); if(!fp) { META_ERROR("ini: Unable to open plugins file '%s': %s", inifile, strerror(errno)); RETURN_ERRNO(mFALSE, ME_NOFILE); } META_LOG("ini: Begin re-reading plugins list: %s", inifile); for(n=0, ln=1; !feof(fp) && fgets(line, sizeof(line), fp) && n < size; ln++) { // Remove line terminations. char *cp; if((cp=strrchr(line, '\r'))) *cp='\0'; if((cp=strrchr(line, '\n'))) *cp='\0'; // Parse into a temp plugin memset(&pl_temp, 0, sizeof(pl_temp)); if(!pl_temp.ini_parseline(line)) { if(meta_errno==ME_FORMAT) META_ERROR("ini: Skipping malformed line %d of %s", ln, inifile); continue; } // Try to find plugin with this pathname in the current list of // plugins. if(!(pl_found=find(pl_temp.pathname))) { // Check for a matching platform with higher platform specifics // level. if(NULL != (pl_found=find_match(&pl_temp))) { if(pl_found->pfspecific >= pl_temp.pfspecific) { META_DEBUG(1, ("ini: Skipping plugin, line %d of %s: plugin with higher platform specific level already exists. (%d >= %d)", ln, inifile, pl_found->pfspecific, pl_temp.pfspecific)); continue; } if(PA_LOAD == pl_found->action) { META_DEBUG(1, ("ini: Plugin in line %d overrides loading of plugin with lower platform specific level %d, ours %d", ln, pl_found->pfspecific, pl_temp.pfspecific)); int _index = pl_found->index; memset(pl_found, 0, sizeof(MPlugin)); pl_found->index = _index; } else { META_DEBUG(1, ("ini: Plugin in line %d should override existing plugin with lower platform specific level %d, ours %d. Unable to comply.", ln, pl_found->pfspecific, pl_temp.pfspecific)); continue; } } // new plugin; add to list if((pl_added=add(&pl_temp))) { // try to load this plugin at the next opportunity pl_added->action=PA_LOAD; } else // error details logged in add() continue; } else { // This plugin is already in the current list of plugins. // Pathname already matches. Recopy desc, if specified in // plugins.ini. if(pl_temp.desc[0] != '<') STRNCPY(pl_found->desc, pl_temp.desc, sizeof(pl_found->desc)); // Check the file to see if it looks like it's been modified // since we last loaded it. if(!pl_found->newer_file()) { if(meta_errno==ME_NOFILE) { META_ERROR("ini: Skipping plugin, couldn't stat file '%s': %s", pl_found->pathname, strerror(errno)); continue; } else { // File hasn't been updated. // Keep plugin (don't let refresh() unload it). pl_found->action=PA_KEEP; } } // Newer file on disk. else if(pl_found->status >= PL_OPENED) { META_DEBUG(2, ("ini: Plugin '%s' has newer file on disk", pl_found->desc)); pl_found->action=PA_RELOAD; } else META_ERROR("ini: Plugin '%s' has newer file, but unexpected status (%s)", pl_found->desc, pl_found->str_status()); } if(NULL != pl_found) { META_LOG("ini: Read plugin config for: %s", pl_found->desc); } else { META_LOG("ini: Read plugin config for: %s", pl_temp.desc); } n++; } META_LOG("ini: Finished reading plugins list: %s; Found %d plugins", inifile, n); fclose(fp); if(!n) { META_ERROR("ini: Warning; no plugins found to load?"); } return(mTRUE); }
mBOOL MPluginList::ini_startup() { FILE *fp; char line[MAX_STRBUF_LEN]; int n, ln; MPlugin *pmatch; if(!valid_gamedir_file(inifile)) { META_ERROR("ini: Metamod plugins file empty or missing: %s", inifile); RETURN_ERRNO(mFALSE, ME_NOFILE); } full_gamedir_path(inifile, inifile); fp=fopen(inifile, "r"); if(!fp) { META_ERROR("ini: Unable to open plugins file '%s': %s", inifile, strerror(errno)); RETURN_ERRNO(mFALSE, ME_NOFILE); } META_LOG("ini: Begin reading plugins list: %s", inifile); for(n=0, ln=1; !feof(fp) && fgets(line, sizeof(line), fp) && n < size; ln++) { // Remove line terminations. char *cp; if((cp=strrchr(line, '\r'))) *cp='\0'; if((cp=strrchr(line, '\n'))) *cp='\0'; // Parse directly into next entry in array if(!plist[n].ini_parseline(line)) { if(meta_errno==ME_FORMAT) META_ERROR("ini: Skipping malformed line %d of %s", ln, inifile); continue; } // Check for a duplicate - an existing entry with this pathname. if(find(plist[n].pathname)) { // Should we check platform specific level here? META_INFO("ini: Skipping duplicate plugin, line %d of %s: %s", ln, inifile, plist[n].pathname); continue; } // Check for a matching platform with different platform specifics // level. if(NULL != (pmatch=find_match(&plist[n]))) { if(pmatch->pfspecific >= plist[n].pfspecific) { META_DEBUG(1, ("ini: Skipping plugin, line %d of %s: plugin with higher platform specific level already exists. (%d >= %d)", ln, inifile, pmatch->pfspecific, plist[n].pfspecific)); continue; } META_DEBUG(1, ("ini: Plugin in line %d overrides existing plugin with lower platform specific level %d, ours %d", ln, pmatch->pfspecific, plist[n].pfspecific)); int _index = pmatch->index; memset(pmatch, 0, sizeof(MPlugin)); pmatch->index = _index; } plist[n].action=PA_LOAD; META_LOG("ini: Read plugin config for: %s", plist[n].desc); n++; endlist=n; // mark end of list } META_LOG("ini: Finished reading plugins list: %s; Found %d plugins to load", inifile, n); fclose(fp); if(!n) { META_ERROR("ini: Warning; no plugins found to load?"); } return(mTRUE); }
mBOOL DLLINTERNAL MConfig::set(option_t *setp, const char *setstr) { char pathbuf[PATH_MAX]; int *optval = (int *) setp->dest; char **optstr = (char **) setp->dest; // cvar_t *optcvar = (cvar_t *) setp->dest; // SETOPT_FN optcmd = (SETOPT_FN) setp->dest; if(!setstr) return(mTRUE); switch(setp->type) { case CF_INT: if(!isdigit(setstr[0])) { META_WARNING("option '%s' invalid format '%s'", setp->name, setstr); RETURN_ERRNO(mFALSE, ME_FORMAT); } *optval=atoi(setstr); META_DEBUG(3, ("set config int: %s = %d", setp->name, *optval)); break; case CF_BOOL: if(strcasematch(setstr, "true") || strcasematch(setstr, "yes") || strmatch(setstr, "1")) { *optval=1; } else if(strcasematch(setstr, "false") || strcasematch(setstr, "no") || strmatch(setstr, "0")) { *optval=0; } else { META_WARNING("option '%s' invalid format '%s'", setp->name, setstr); RETURN_ERRNO(mFALSE, ME_FORMAT); } META_DEBUG(3, ("set config bool: %s = %s", setp->name, *optval ? "true" : "false")); break; case CF_STR: if(*optstr) free(*optstr); *optstr=strdup(setstr); META_DEBUG(3, ("set config string: %s = %s", setp->name, *optstr)); break; case CF_PATH: if(*optstr) free(*optstr); full_gamedir_path(setstr, pathbuf); *optstr=strdup(pathbuf); META_DEBUG(3, ("set config path: %s = %s", setp->name, *optstr)); break; #if 0 case CF_CVAR: CVAR_SET_STRING(optcvar->name, setstr); META_DEBUG(3, ("set config cvar: %s = %s", optcvar->name, setstr)); break; case CF_CMD: optcmd(setp->name, setstr); META_DEBUG(3, ("set config command: %s, %s", optcvar->name, setstr)); break; #endif default: META_WARNING("unrecognized config type '%d'", setp->type); RETURN_ERRNO(mFALSE, ME_ARGUMENT); } return(mTRUE); }