#include <stdlib.h> #include <dirent.h> #include <string.h> #include <sys/stat.h> #include <malloc.h> #ifndef CYGWIN32 #include <unistd.h> #endif #include "scripting.h" #include "mud.h" extern int errno; DIRFILE_ENTRY * dirfile_root; DIRFILE_ENTRY * new_dir_entry() { DIRFILE_ENTRY * new; new = malloc(sizeof(*new)); new->name = strdup(""); new->creator = strdup(""); new->vSecurity = 5; new->eSecurity = 5; new->cSecurity = 5; new->parent = NULL; new->contents = NULL; return new; } void free_dir_entry(DIRFILE_ENTRY *target) { free(target->name); free(target->creator); /*free the list stuff, dont' need it yet, can't delete dir's.*/ free(target); } bool read_dirfile(LIST * target,const char * path, DIRFILE_ENTRY * parent) { FILE * fp; char filename[MAX_BUFFER]; char buf[MAX_BUFFER]; char * word; ITERATOR pIter; bool done = FALSE; bool found; DIRFILE_ENTRY *current; sprintf(filename, "%s/dir", path); if ((fp = fopen(filename, "r")) == NULL) return FALSE; word = fread_word(fp); while (!done) { found = FALSE; switch (word[0]) { case 'B': current = new_dir_entry(); current->contents = NULL; if (parent == NULL) { /*If a directory is somehow orphaned, link it to root.*/ current->parent = dirfile_root; } else { current->parent = parent; } current->locked = FALSE; free(current->name); free(current->creator); found = TRUE; break; case 'n': SREAD("name", current->name); break; case 'c': SREAD("creator", current->creator); IREAD("csec", current->cSecurity); break; case 'e': IREAD("esec", current->eSecurity); break; case 't': IREAD("type", current->type); break; case 'v': IREAD("vsec", current->vSecurity); break; case 'E': if (!strcasecmp(word, "EOF")) {done = TRUE; found = TRUE; break;} if (!strcasecmp(word, "END")) { AttachToList(current, target); sprintf(buf, "%s/%s", path, current->name); current->realpath = strdup(buf); found = TRUE; } break; } if (!found) { bug("read_dirfile: unexpected '%s' in %s's dirfile.", word, path); } /* read one more */ if (!done) { word = fread_word(fp); } } fclose(fp); AttachIterator(&pIter, target); while ((current = (DIRFILE_ENTRY *) NextInList(&pIter)) != NULL) { if (current->type == DIRFILE_TYPE_DIR && strcasecmp(current->name, ".")) { sprintf(buf, "%s/%s", path, current->name); if (current->contents == NULL) current->contents = AllocList(); read_dirfile(current->contents, buf, current); } } DetachIterator(&pIter); return TRUE; } void init_dir_structure() { dirfile_root = new_dir_entry(); free(dirfile_root->name); dirfile_root->name = strdup("/"); dirfile_root->contents = AllocList(); dirfile_root->realpath = SCRIPT_DIR; if (!read_dirfile(dirfile_root->contents, SCRIPT_DIR, NULL)) log_string("read_dirfile failed!"); } DIRFILE_ENTRY * get_entry_by_name(const char * name, LIST * target) { ITERATOR pIter; DIRFILE_ENTRY *current; if (target == NULL) return NULL; AttachIterator(&pIter, target); while ((current = (DIRFILE_ENTRY *) NextInList(&pIter)) != NULL) { if (!strcasecmp(current->name, name)) { DetachIterator(&pIter); return current; } } DetachIterator(&pIter); return NULL; } DIRFILE_ENTRY * get_dirfile_from_path(const char * path, DIRFILE_ENTRY *start, D_MOBILE *dMob) { char *lPath; char *token; DIRFILE_ENTRY *current; char buf[MAX_BUFFER]; lPath = strdup(path); token = strtok(lPath, "/"); if (lPath[0] != '/') { current = start; } else { current = dirfile_root; if (token == NULL) { free(lPath); return current; } } if (!strcasecmp(token, "~")) { sprintf(buf, "/home/%s",dMob->name ); current = get_dirfile_from_path(buf, NULL, NULL); token = strtok(NULL, "/"); } while (token != NULL) { if (strcasecmp(token, "..")) current = get_entry_by_name(token, current->contents); else current = current->parent; if (current == NULL) { return NULL; } token = strtok(NULL, "/"); } free(lPath); return current; } DIRFILE_ENTRY * get_dirfile_from_real_path(const char* path, D_MOBILE *dMob) { char * nPath; nPath = strpbrk(path, "/"); nPath++; nPath = strdup(nPath); nPath = strpbrk(nPath, "/"); return get_dirfile_from_path(nPath, NULL, dMob); } void write_dirfile(LIST * target) { DIRFILE_ENTRY * current; ITERATOR pIter; char filename[MAX_BUFFER]; FILE *fp; current = get_entry_by_name(".", target); if (current == NULL) { bug("Null current in write_dirfile"); return; } if (current->type == DIRFILE_TYPE_FILE) { bug("Wrong type in write_dirfile"); return; } sprintf(filename, "%s/dir", current->realpath); if ((fp = fopen(filename, "w")) == NULL) { bug("Unable to write to %s's dirfile", current->realpath); return; } AttachIterator(&pIter, target); while ((current = (DIRFILE_ENTRY *) NextInList(&pIter)) != NULL) { fprintf(fp, "BEGIN \n"); fprintf(fp, "name %s~\n", current->name); fprintf(fp, "creator %s~\n", current->creator); fprintf(fp, "vsec %d\n", current->vSecurity); fprintf(fp, "esec %d\n", current->eSecurity); fprintf(fp, "csec %d\n", current->cSecurity); fprintf(fp, "type %d\n", current->type); fprintf(fp, "END \n"); } fprintf(fp, "EOF "); fclose(fp); } void handle_file_editing(D_MOBILE *dMob, char *arg) { FILE *fp; int line; char filename[MAX_BUFFER]; char cmd[MAX_BUFFER]; char * orig; char arg2[MAX_BUFFER]; orig = strdup(arg); arg = one_arg(arg, cmd); if (!strcasecmp(cmd, "*")) { text_to_mobile(dMob, "Leaving editor, file unsaved.\n\r"); dMob->editing->locked = FALSE; buffer_free(dMob->editing->data); dMob->editing = NULL; free(orig); return; } if (!strcasecmp(cmd, "@")) { sprintf(filename, "%s",dMob->editing->realpath); text_to_mobile(dMob, "Leaving editor, file saved.\n\r"); if ((fp = fopen(filename, "w")) == NULL) { bug("Unable to write to file %s", dMob->editing->realpath); return; } fprintf(fp, "%s", dMob->editing->data->data); fclose(fp); dMob->editing->locked = FALSE; buffer_free(dMob->editing->data); dMob->editing = NULL; free(orig); return; } if (!strcasecmp(cmd, ".s")) { free(orig); numbered_buffer_to_mobile(dMob->editing->data, dMob); return; } if (!strcasecmp(cmd, ".bl")) { free(orig); buffer_strcat(dMob->editing->data, "\n"); return; } if (!strcasecmp(cmd, ".ld")) { free(orig); line = atoi(arg); log_string("%d is the line.", line); if (line == 0) { text_to_mobile(dMob, "syntax: .ld <line number>\n\r"); } else { buffer_remline(dMob->editing->data, line); text_to_mobile(dMob, "Ok.\n\r"); } return; } if (!strcasecmp(cmd, ".li")) { free(orig); arg = one_arg(arg, arg2); line = atoi(arg2); if (line == 0 || arg[0] == '\0') { text_to_mobile(dMob, "syntax: .li <line number> <text>\n\r"); } else { buffer_insertline(dMob->editing->data, line, arg); text_to_mobile(dMob, "Ok.\n\r"); } return; } if (!strcasecmp(cmd, ".lr")) { free(orig); arg = one_arg(arg, arg2); line = atoi(arg2); if (line == 0 || arg[0] == '\0') { text_to_mobile(dMob, "syntax: .lr <line number> <text>\n\r"); } else { buffer_replaceline(dMob->editing->data, line, arg); text_to_mobile(dMob, "Ok.\n\r"); } return; } buffer_strcat(dMob->editing->data, orig); /*buffer_strcat(dMob->editing->data, "\n");*/ free(orig); } bool file_exists(const char * filename) { struct stat fStats; if (stat(filename, &fStats) == -1) { if (errno == ENOENT) return FALSE; } if (S_ISREG(fStats.st_mode)) return TRUE; return FALSE; } void load_code(LIST * target) { ITERATOR pIter; DIRFILE_ENTRY *current; if (target == NULL) return; AttachIterator(&pIter, target); while ((current = (DIRFILE_ENTRY *) NextInList(&pIter)) != NULL) { if (current->type == DIRFILE_TYPE_FILE && is_postfix(current->name, ".lua")) { log_string("loading file: %s ", current->realpath); if (luaL_loadfile(lua, current->realpath) == 0) { if (lua_pcall(lua, 0, 0, 0) != 0) { log_string("error running file. error: %s", lua_tostring(lua, -1)); } } else { log_string("Error loading file. Error: %s", lua_tostring(lua, -1)); } } } DetachIterator(&pIter); /*Split into two loops so code is loaded one directory at a time, top down.*/ AttachIterator(&pIter, target); while ((current = (DIRFILE_ENTRY *) NextInList(&pIter)) != NULL) { if (!strcmp(current->name, "home")) { log_string("Skipping home dir."); continue; } if (current->type == DIRFILE_TYPE_DIR) { load_code(current->contents); } } DetachIterator(&pIter); } bool can_access(D_MOBILE *dMob, DIRFILE_ENTRY *target, int type) { bool access = FALSE; if (!strcasecmp(dMob->name, target->creator)) access = TRUE; switch (type) { case ESECURITY: if (dMob->level >= target->eSecurity) access=TRUE; break; case VSECURITY: if (dMob->level >= target->vSecurity) access=TRUE; break; case CSECURITY: if (dMob->level >= target->cSecurity) access=TRUE; break; } return access; } /* shell commands below here. */ void cmd_chmod(D_MOBILE *dMob, char * arg) { char filename[MAX_BUFFER]; char type[MAX_BUFFER]; DIRFILE_ENTRY * file; int rank; arg = one_arg(arg, filename); arg = one_arg(arg, type); rank = atoi(arg); file = get_entry_by_name(filename, dMob->curdir->contents); if (file == NULL) { text_to_mobile(dMob, "File not found.\n]r"); return; } if (rank > dMob->level) { text_to_mobile(dMob, "You cannot set the rank higher than your own.\n\r"); return; } if (!can_access(dMob, file, ESECURITY)) { text_to_mobile(dMob, "Insufficent security"); return; } switch(type[0]) { case 'v': case 'V': file->vSecurity = rank; break; case 'E': case 'e': file->eSecurity = rank; break; case 'C': case 'c': file->cSecurity = rank; break; default: text_to_mobile(dMob, "Incorrect type, use v,c, or e.\n\r"); return; } text_to_mobile(dMob, "Value set.\n\r"); write_dirfile(dMob->curdir->contents); } void cmd_chown(D_MOBILE *dMob, char * arg) { DIRFILE_ENTRY * current; char arg2[MAX_BUFFER]; D_MOBILE *target; arg = one_arg(arg, arg2); if (arg[0] == '\0' || arg2[0] == '\0') { text_to_mobile(dMob,"Syntax: chown <target> <new owner>\n\r"); return; } current = get_entry_by_name(arg2, dMob->curdir->contents); target = get_mobile_by_name(arg); if (target == NULL) { text_to_mobile(dMob, "Set owner to who?\n\r"); return; } if (!can_access(dMob, current, ESECURITY)) { text_to_mobile(dMob, "Insufficent security"); return; } free(current->creator); current->creator = strdup(target->name); text_to_mobile(dMob, "Owner changed.\n\r"); write_dirfile(dMob->curdir->contents); } void cmd_mkcore(D_MOBILE *dMob, char * arg) { DIRFILE_ENTRY * current; if (arg[0] == '\0') { text_to_mobile(dMob,"Syntax: mkcore <target>\n\r"); return; } current = get_entry_by_name(arg, dMob->curdir->contents); if (current == NULL) { text_to_mobile(dMob, "File not found.\n\r"); return; } if (!can_access(dMob, current, ESECURITY)) { text_to_mobile(dMob, "Insufficent security"); return; } free(current->creator); current->creator = strdup("core"); write_dirfile(dMob->curdir->contents); text_to_mobile(dMob, "File added to core.\n\r"); } void cmd_demote(D_MOBILE *dMob, char *arg) { D_MOBILE * target; char buf[MAX_BUFFER]; int rank; arg = one_arg(arg, buf); rank = atoi(arg); if (arg[0] == '\0') { text_to_mobile(dMob, "Syntax: demote <target> <rank>\n\r"); return; } target = get_mobile_by_name(buf); if (target == NULL) { text_to_mobile(dMob, "They're not here.\n\r"); return; } if (target->level >= dMob->level) { text_to_mobile(dMob, "You can't demote them.\n\r"); log_string("%s tried to demote %s to rank %d", dMob->name,target->name, rank); return; } if (rank < 1) rank = 1; target->level = rank; text_to_mobile(dMob, "Ok.\n\r"); text_to_mobile(target, "You have been demoted.\n\r"); } void cmd_promote(D_MOBILE *dMob, char *arg) { D_MOBILE * target; char buf[MAX_BUFFER]; char path[MAX_BUFFER]; struct stat stats; DIRFILE_ENTRY *home; DIRFILE_ENTRY *new; DIRFILE_ENTRY *first; int rank; arg = one_arg(arg, buf); rank = atoi(arg); if (buf[0] =='\0' || rank == 0) { text_to_mobile(dMob, "Promote who to what rank?\n\r"); return; } /* if (rank >= dMob->level) { text_to_mobile(dMob, "You can't promote people to that level.\n\r"); return; }*/ target = get_mobile_by_name(buf); if (target == NULL) { text_to_mobile(dMob, "No such player found.\n\r"); return; } if (target == dMob) { text_to_mobile(dMob, "Nice try.\n\r"); log_string("%s tried to promote themselves to level %d", dMob->name, rank); return; } if (target->level > rank) { text_to_mobile(dMob, "Use the demote command.\n\r"); return; } sprintf(path, "%s/%s", IMP_HOME_DIR, target->name); home = get_dirfile_from_path (path, NULL, NULL); target->level = rank; if (home != NULL) { text_to_mobile(dMob, "That home directory is already in use, attaching player.\n\r"); text_to_mobile(target, "You have been promoted to shell access.\n\r"); log_string("%s has been promoted to level %d.", target->name, target->level); } home = get_dirfile_from_path(IMP_HOME_DIR, NULL, NULL); new = new_dir_entry(); new->parent =home; AttachToList(new, home->contents); sprintf(buf, "%s/%s", home->realpath, target->name ); new->name = strdup(target->name); new->creator = strdup(target->name); new->cSecurity = target->level; new->vSecurity = target->level; new->eSecurity = target->level; new->type = DIRFILE_TYPE_DIR; new->contents = AllocList(); new->realpath = strdup(buf); log_string(buf); /* . entry in new dir.*/ first = new_dir_entry(); first->name = strdup("."); first->cSecurity = target->level; first->vSecurity = target->level; first->eSecurity = target->level; first->type = DIRFILE_TYPE_DIR; first->realpath = strdup(buf); AttachToList(first, new->contents); stats.st_mode = S_IRWXU; mkdir(buf, stats.st_mode); write_dirfile(home->contents); write_dirfile(new->contents); text_to_mobile(dMob, "They have been promoted.\n\r"); text_to_mobile(target, "You have been promoted to shell access.\n\r"); log_string("%s has been promoted to rank %d.", target->name, target->level); return; } void cmd_edit(D_MOBILE *dMob, char * arg) { DIRFILE_ENTRY * current; FILE *fp; char * file; /*int c; char buf[MAX_BUFFER];*/ if (dMob->curdir == NULL) { log_string("Null curdir in cmd_edit for mobile %d", dMob->name); return; } current = get_entry_by_name(arg, dMob->curdir->contents); if (current == NULL || current->type == DIRFILE_TYPE_DIR) { text_to_mobile(dMob, "File not found.\n\r"); return; } if (!can_access(dMob, current,ESECURITY)) { text_to_mobile(dMob, "Insufficent security.\n\r"); return; } dMob->editing = current; dMob->editing->data = buffer_new(MAX_BUFFER); buffer_clear(dMob->editing->data); if (file_exists(dMob->editing->realpath)) { if ((fp = fopen(dMob->editing->realpath, "r")) == NULL) { text_to_mobile(dMob, "Error viewing file.\n\r"); return; } file= read_lua_file(fp); buffer_strcat(dMob->editing->data, file); fclose(fp); } numbered_buffer_to_mobile(dMob->editing->data, dMob); current->locked = TRUE; } void cmd_ls(D_MOBILE *dMob, char * arg) { ITERATOR pIter; DIRFILE_ENTRY * current; char buf[MAX_BUFFER]; if (arg[0] =='\0') { if (dMob->curdir == NULL) /*root dir.*/ { log_string("Null curdir for mob %s", dMob->name); } else AttachIterator(&pIter, dMob->curdir->contents); text_to_mobile(dMob, " E V C Creator\n\r"); while ((current = (DIRFILE_ENTRY *) NextInList(&pIter)) != NULL) { if (current->type == DIRFILE_TYPE_DIR) { sprintf(buf, "#B%-20s %d %d %d %s\n\r", current->name, current->eSecurity, current->vSecurity, current->cSecurity, current->creator); } else { sprintf(buf, "#r%-20s %d %d %d %s\n\r", current->name, current->eSecurity, current->vSecurity, current->cSecurity, current->creator); } text_to_mobile(dMob, buf); } } else { current = get_dirfile_from_path(arg, dMob->curdir, NULL); if (current == NULL || current->type == DIRFILE_TYPE_FILE) { text_to_mobile(dMob,"Directory not found."); return; } AttachIterator(&pIter, current->contents); while ((current = (DIRFILE_ENTRY *) NextInList(&pIter)) != NULL) { if (current->type == DIRFILE_TYPE_DIR) { sprintf(buf, "#B%s\n\r", current->name); } else { sprintf(buf, "#r%s\n\r", current->name); } text_to_mobile(dMob, buf); } } } void cmd_rmdir(D_MOBILE *dMob, char * arg) { DIRFILE_ENTRY * target; DIR * dir; struct dirent * entry; int count; char buf[MAX_BUFFER]; target = get_entry_by_name(arg, dMob->curdir->contents); if (target == NULL || target->type == DIRFILE_TYPE_FILE) { text_to_mobile(dMob, "That directory does not exist.\n\r"); return; } if (!can_access(dMob, target, CSECURITY)) { text_to_mobile(dMob, "You do not have enough security.\n\r"); return; } if (target == dMob->curdir) { text_to_mobile(dMob, "Now how would that work??\n\r"); return; } /*check emptyness except for Dir file. * If it's the only file there, remove it, so rmdir can work. */ count = 0; dir=opendir(target->realpath); while ((entry = readdir(dir))!= NULL) { if (!strcasecmp(".", entry->d_name) || !strcasecmp("..", entry->d_name) || !strcasecmp("dir", entry->d_name)) { continue; } else count++; } closedir(dir); if (count >0) { text_to_mobile(dMob, "That directory is not empty.\n\r"); return; } else { sprintf(buf, "%s/dir",target->realpath); if (unlink(buf) == -1) { bug("Error deleting file: %s", buf); } } if (rmdir(target->realpath) != 0 ) { if (errno == EEXIST || errno == ENOTEMPTY) { text_to_mobile(dMob, "The directory is not empty.\n\r"); return; } bug("Error deleting file: %s", target->realpath); } DetachFromList(target, dMob->curdir->contents); write_dirfile(dMob->curdir->contents); free_dir_entry(target); } void cmd_mkdir(D_MOBILE *dMob, char * arg) { DIRFILE_ENTRY * current; DIRFILE_ENTRY * new; DIRFILE_ENTRY * first; struct stat stats; char buf[MAX_BUFFER]; current = dMob->curdir; new = get_entry_by_name(arg, current->contents); if (new != NULL) { text_to_mobile(dMob, "That name is taken.\n\r"); return; } if (can_access(dMob, current, CSECURITY)) { new = new_dir_entry(); new->parent =current; AttachToList(new, current->contents); sprintf(buf, "%s/%s", current->realpath, arg ); new->name = strdup(arg); new->creator = strdup(dMob->name); new->cSecurity = dMob->level; new->vSecurity = dMob->level; new->eSecurity = dMob->level; new->type = DIRFILE_TYPE_DIR; new->contents = AllocList(); new->realpath = strdup(buf); log_string(buf); /* . entry in new dir.*/ first = new_dir_entry(); first->name = strdup("."); first->cSecurity = dMob->level; first->vSecurity = dMob->level; first->eSecurity = dMob->level; first->type = DIRFILE_TYPE_DIR; first->realpath = strdup(buf); AttachToList(first, new->contents); stats.st_mode = S_IRWXU; mkdir(buf, stats.st_mode); write_dirfile(current->contents); write_dirfile(new->contents); return; } log_string("insufficient security."); } void cmd_touch(D_MOBILE *dMob, char * arg) { DIRFILE_ENTRY * current; DIRFILE_ENTRY * new; char buf[MAX_BUFFER]; current = dMob->curdir; new = get_entry_by_name(arg, current->contents); if (new != NULL) { text_to_mobile(dMob, "That name is taken.\n\r"); return; } if (can_access(dMob, current, CSECURITY)) { new = new_dir_entry(); new->parent =current; AttachToList(new, current->contents); sprintf(buf, "%s/%s", current->realpath, arg ); new->name = strdup(arg); new->creator = strdup(dMob->name); new->cSecurity = dMob->level; new->vSecurity = dMob->level; new->eSecurity = dMob->level; new->type = DIRFILE_TYPE_FILE; new->realpath = strdup(buf); log_string(buf); write_dirfile(current->contents); return; } log_string("insufficient security."); } void cmd_cd(D_MOBILE *dMob, char * arg) { DIRFILE_ENTRY * current; if (!strcasecmp(arg, "..")) { if (dMob->curdir->parent ==NULL) { text_to_mobile(dMob,"No such path.\n\r"); return; } if (can_access(dMob, dMob->curdir->parent, VSECURITY)) { dMob->curdir = dMob->curdir->parent; return; } else { text_to_mobile(dMob,"You're not allowed to view that directory.\n\r"); return; } } current = get_dirfile_from_path(arg, dMob->curdir, dMob); if (current == NULL || current->type == DIRFILE_TYPE_FILE) { text_to_mobile(dMob, "path not found."); return; } if (can_access(dMob, current, VSECURITY)) { dMob->curdir = current; } else { text_to_mobile(dMob,"You're not allowed to view that directory.\n\r"); return; } } void cmd_rm(D_MOBILE *dMob, char * arg) { DIRFILE_ENTRY * target; target = get_entry_by_name(arg, dMob->curdir->contents); if (target == NULL || target->type == DIRFILE_TYPE_DIR) { text_to_mobile(dMob, "That file does not exist.\n\r"); return; } if (!can_access(dMob, target, CSECURITY)) { text_to_mobile(dMob, "You do not have enough security.\n\r"); return; } if (target->locked) { text_to_mobile(dMob, "That file is busy.\n\r"); return; } DetachFromList(target, dMob->curdir->contents); write_dirfile(dMob->curdir->contents); if (file_exists(target->realpath) == TRUE && unlink(target->realpath) == -1) { bug("Error deleting file: %s", target->realpath); } free_dir_entry(target); } void cmd_view(D_MOBILE *dMob, char * arg) { DIRFILE_ENTRY * target; FILE *fp; char * file; target = get_entry_by_name(arg, dMob->curdir->contents); if (target == NULL || target->type == DIRFILE_TYPE_DIR) { text_to_mobile(dMob, "That file does not exist.\n\r"); return; } if (!can_access(dMob, target, VSECURITY)) { text_to_mobile(dMob, "You do not have enough security.\n\r"); return; } if ((fp = fopen(target->realpath, "r")) == NULL) { text_to_mobile(dMob, "Error viewing file.\n\r"); return; } file = read_lua_file(fp); text_to_mobile(dMob, file); fclose(fp); } void cmd_reload(D_MOBILE *dMob, char *arg) { /*TODO: Add refresh info to lua class superstructure*/ DIRFILE_ENTRY * current; current = get_entry_by_name(arg, dMob->curdir->contents); if (current == NULL || current->type == DIRFILE_TYPE_DIR) { text_to_mobile(dMob, "File not found.\n\r"); return; } if (!can_access(dMob, current, ESECURITY)) { text_to_mobile(dMob, "Insufficent security.\n\r"); return; } log_string("reloading file: %s ", current->realpath); if (luaL_loadfile(lua, current->realpath) == 0) { if (lua_pcall(lua, 0, 0, 0) != 0) { log_string("error while reloading file. error: %s", lua_tostring(lua, -1)); } } else { log_string("Error reloading file. Error: %s", lua_tostring(lua, -1)); } } void cmd_return(D_MOBILE *dMob, char * arg) { dMob->shell = FALSE; }