#include "global.h" #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <setjmp.h> #include <errno.h> #if defined(sun) #include <alloca.h> #endif /* unistd.h defines _POSIX_VERSION on POSIX.1 systems. */ #if defined(DIRENT) || defined(_POSIX_VERSION) #include <dirent.h> #define NLENGTH(dirent) (strlen((dirent)->d_name)) #else /* not (DIRENT or _POSIX_VERSION) */ #define dirent direct #define NLENGTH(dirent) ((dirent)->d_namlen) #ifdef SYSNDIR #include <sys/ndir.h> #endif /* SYSNDIR */ #ifdef SYSDIR #include <sys/dir.h> #endif /* SYSDIR */ #ifdef NDIR #include <ndir.h> #endif /* NDIR */ #endif /* not (DIRENT or _POSIX_VERSION) */ #include "array.h" #include "simulate.h" #include "interpret.h" #include "object.h" #include "sent.h" #include "exec.h" #include "comm.h" #include "main.h" #include "dbase_efuns.h" #include "stralloc.h" #include "call_out.h" #include "lex.h" #include "backend.h" #include "dynamic_buffer.h" #include "otable.h" #include "socket_efuns.h" #include "simul_efun.h" extern int errno; extern int comp_flag; char *inherit_file; #ifdef _DCC #define strcasecmp stricmp #endif #ifndef HAVE_UNISTD_H extern int readlink PROT((char *, char *, int)); extern int symlink PROT((char *, char *)); extern int fchmod PROT((int, int)); #endif #ifdef AMIGA_PORT #define lstat stat #endif char *last_verb; extern int special_parse PROT((char *)), legal_path PROT((char *)); extern void pop_stack(); char **lfuns=NULL; int num_lfuns=0; void remove_all_players(), end_new_file(), print_svalue PROT((struct svalue *)), destruct2(); extern int d_flag,current_time; extern struct svalue *sp; struct object *obj_list, *obj_list_destruct, *master_ob; struct object *current_object; /* The object interpreting a function. */ struct object *command_giver; /* Where the current command came from. */ int num_parse_error; /* Number of errors in the parser. */ int new_clone_number; struct variable *find_status(char *str,int must_find) { int i; if((str=findstring(str))) { for (i=0; i < current_object->prog->num_variables; i++) { if (current_object->prog->variable_names[i].name==str) return ¤t_object->prog->variable_names[i]; } } if (!must_find) return 0; error("--Status %s not found in prog for %s\n", str, current_object->prog->name); return 0; } /* * Give the correct uid and euid to a created object. */ void give_uid_to_object(struct object *ob) { struct svalue *ret; char *creator_name; if (!current_object || !current_object->user) { /* * Only temporary. */ ob->user = make_shared_string("NONAME"); ob->eff_user = 0; } if (master_ob == 0) { ret=apply("get_root_uid",ob,0,1); if (ret == 0 || ret->type != T_STRING) { fprintf(stderr, "get_root_uid() in secure/master.c does not work\n"); exit(1); } if(ob->user) free_string(ob->user); ob->user = copy_shared_string(strptr(ret)); if(ob->eff_user) free_string(ob->eff_user); ob->eff_user=copy_shared_string(ob->user); master_ob=ob; add_ref(ob,"master object"); return; } /* * Ask master.c who the creator of this object is. */ push_shared_string(copy_shared_string(ob->prog->name)); APPLY_MASTER_OB(ret=,"creator_file", 1); if (!ret) error("No function 'creator_file' in master.c!\n"); if (ret->type != T_STRING) { /* This can be the case for objects in /ftp and /open. */ destruct_object(ob); error("Illegal object to load.\n"); } creator_name = strptr(ret); if(ob->user) free_string(ob->user); ob->user = copy_shared_string(creator_name); if(ob->eff_user) free_string(ob->eff_user); ob->eff_user = copy_shared_string(creator_name); return; } #define IS_POSTFIX(X,Y) (strlen(X)>=strlen(Y) && !strcmp((X)+strlen(X)-strlen(Y),(Y))) /* * Load an object definition from file. If the object wants to inherit * from an object that is not loaded, discard all, load the inherited object, * and reload again. * */ struct program *load_object(char *lname,struct inherit_check *inherit_err) { FILE *f; extern int total_lines; extern int approved_object; extern struct program *prog; extern char *current_file; struct stat c_st; int name_length; char *name,*real_name; struct inherit_check i_check,*cnt; if (current_object && current_object->eff_user == 0) error("Can't load objects when no effective user.\n"); /* Truncate possible .c in the object name. */ /* Remove leading '/' if any. */ if(!batch_mode) { while(lname[0] == '/') lname++; name_length = strlen(lname); name=(char *)alloca(name_length+1); strcpy(name, lname); }else{ real_name=combine_path(0,lname); name_length=strlen(real_name); name=(char *)alloca(name_length+1); strcpy(name,real_name); free(real_name); } real_name=(char *)alloca(name_length+strlen(LPC_SUFFIX)+6); if (IS_POSTFIX(name,LPC_SUFFIX)) { (void)strcpy(real_name, name); if(batch_mode) { name[name_length-2] = '\0'; name_length -= 2; } }else{ (void)strcpy(real_name, name); if(!batch_mode) strcat(real_name,LPC_SUFFIX); } /* * First check that the c-file exists. */ if (stat(real_name, &c_st) == -1) { if(batch_mode) { strcat(real_name,".c"); if (stat(real_name, &c_st) == -1) return 0; }else{ return 0; } } for(cnt=inherit_err;cnt;cnt=cnt->next) { if (!strcmp(name, cnt->name)) { /* hmm, we might loose the memory of inherit_file here */ error("Illegal to inherit self.\n"); } } /* * Check if it's a legal name. */ if (!legal_path(real_name)) { fprintf(stderr, "Illegal pathname: %s\n", real_name); error("Illegal path name.\n"); return 0; } if (comp_flag) fprintf(stderr, " compiling %s ...", real_name); f = fopen(real_name, "r"); if (f == 0) { perror(real_name); error("Couldn't read the file.\n"); } start_new_file(f); current_file = string_copy(name); /* This one is freed below */ compile_file(); end_new_file(); if (comp_flag) fprintf(stderr, " done\n"); update_compile_av(total_lines); total_lines = 0; (void)fclose(f); free(current_file); current_file = 0; /* Sorry, can't handle objects without programs yet. */ if (inherit_file == 0 && (num_parse_error > 0 || prog == 0)) { if (prog) free_prog(prog, 1); if(num_parse_error == 0 && prog == 0) error("No program in object !\n"); error("Error in loading object\n"); } /* * This is an iterative process. If this object wants to inherit an * unloaded object, then discard current object, load the object to be * inherited and reload the current object again. The global variable * "inherit_file" will be set by lang.y to point to a file name. */ if (inherit_file) { char *tmp = inherit_file; if (prog) { free_prog(prog, 1); prog = 0; } i_check.name=name; i_check.next=inherit_err; inherit_file = 0; if(!load_object(tmp,&i_check)) { error("Couldn't find program '%s' for inherit.\n",tmp); } free(tmp); return load_object(name,0); } /* * Can we approve of this object ? */ prog->flags=ref_cycle; if (approved_object || batch_mode) { prog->flags |= O_APPROVED; prog->flags |= O_APPROVES; }else{ struct svalue *ret; if(!master_ob) { prog->flags |= O_APPROVED; }else{ if(!(prog->flags & O_APPROVED)) { push_shared_string(copy_shared_string(prog->name)); APPLY_MASTER_OB(ret=,"query_approve",1); if(ret) { if(ret->type==T_NUMBER && ret->u.number>0) { prog->flags |= O_APPROVED; if(ret->u.number>1) prog->flags |= O_APPROVES; } } } } } if(!(prog->flags & O_APPROVED)) { free_prog(prog,1); error("Object not approved %s\n",name); } enter_program_hash(prog); /* add name to fast object lookup table */ if (function_exists_in_prog("clean_up",prog)) prog->flags |= O_WILL_CLEAN_UP; if (d_flag > 1 && prog) debug_message("--%s loaded\n", name); return prog; } struct program *make_object(char *str) { extern int total_lines; extern int approved_object; extern struct program *prog; extern char *current_file; char name[200]; struct inherit_check i_check; if (current_object && current_object->eff_user == 0) error("Can't create objects when no effective user.\n"); if(batch_mode) sprintf(name,"/tmp/%s/%d",current_object->eff_user,++new_clone_number); else sprintf(name,"tmp/%s/%d",current_object->eff_user,++new_clone_number); if (comp_flag) fprintf(stderr, " compiling %s ...", name); start_new_string(str,strlen(str)); current_file = string_copy(name); /* This one is freed below */ compile_file(); end_new_file(); if (comp_flag) fprintf(stderr, " done\n"); update_compile_av(total_lines); total_lines = 0; free(current_file); current_file = 0; /* Sorry, can't handle objects without programs yet. */ if (inherit_file == 0 && (num_parse_error > 0 || prog == 0)) { if (prog) free_prog(prog, 1); if (num_parse_error == 0 && prog == 0) error("No program in object !\n"); error("Error in loading object\n"); } /* * This is an iterative process. If this object wants to inherit an * unloaded object, then discard current object, load the object to be * inherited and reload the current object again. The global variable * "inherit_file" will be set by lang.y to point to a file name. */ if (inherit_file) { char *tmp = inherit_file; if (prog) { free_prog(prog, 1); prog = 0; } i_check.name=name; i_check.next=0; inherit_file = 0; if(!load_object(tmp,&i_check)) { error("Couldn't find program '%s' for inherit.\n",tmp); } free(tmp); return make_object(str); } prog->flags=ref_cycle; /* * Can we approve of this object ? */ if (approved_object || batch_mode) { prog->flags |= O_APPROVED; prog->flags |= O_APPROVES; }else{ struct svalue *ret; push_shared_string(copy_shared_string(prog->name)); APPLY_MASTER_OB(ret=,"query_approve",1); if(ret) { if(ret->type==T_NUMBER && ret->u.number>0) { prog->flags |= O_APPROVED; if(ret->u.number>1) prog->flags |= O_APPROVES; } } } if(!(prog->flags & O_APPROVED)) { free_prog(prog,1); error("Object not approved %s\n",name); } enter_program_hash(prog); /* add name to fast object lookup table */ if (function_exists_in_prog("clean_up",prog)) prog->flags |= O_WILL_CLEAN_UP; if (d_flag > 1 && prog) debug_message("--%s loaded\n", name); return prog; } /* * Save the command_giver, because reset() in the new object might change * it. */ struct object *clone_object(char *str1,int nameit) { struct object *new_ob; struct program *p; if(current_object && current_object->eff_user == 0) error("Illegal to call clone_object() with effective user 0\n"); p=find_program(str1); if(!p) { struct svalue *ret; push_new_shared_string(str1); APPLY_MASTER_OB(ret=,"compile_object",1); if(!ret || ret->type!=T_OBJECT) { error("Couldn't clone object '%s' does file exist?\n",str1); } new_ob=ret->u.ob; if(nameit) { new_ob->obj_index=make_shared_string(str1); enter_my_object_hash(new_ob); } return new_ob; } new_ob = get_empty_object(p); if(nameit) { new_ob->obj_index=make_shared_string(str1); enter_my_object_hash(new_ob); } give_uid_to_object(new_ob); reset_object(new_ob, 0); /* Never know what can happen ! :-( */ if (new_ob->flags & O_DESTRUCTED) return 0; return new_ob; } struct object *environment(struct svalue *arg) { struct object *ob = current_object; if (arg && arg->type == T_OBJECT) ob = arg->u.ob; if (ob == 0 || ob->super == 0 || (ob->flags & O_DESTRUCTED)) return 0; if (ob->flags & O_DESTRUCTED) error("environment() of destructed object.\n"); return ob->super; } int tot_alloc_sentence=0; void free_sentence(struct sentences *s,int slot) { free_string(s->sent[slot].match); free_svalue(&(s->sent[slot].function)); s->sent[slot].match=NULL; s->used_slots--; } #ifdef DEBUG void test_sentence(struct sentences *s) { int e,num,num2; char *pos; if(s->used_slots>s->slots || s->slots>tot_alloc_sentence) fatal("Not a valid sentence structure.\n"); num2=0; for(e=0;e<s->slots;e++) { if(!s->sent[e].match) continue; if(debug_findstring(s->sent[e].match)!=s->sent[e].match) fatal("Sentence not in shared string table.\n"); for(num=0,pos=s->sent[e].match-1;(pos=STRCHR(pos+1,'%'));num++); if(num!=s->sent[e].num_arg) { fatal("Error in sentence.\n"); } num2++; } if(num2!=s->used_slots) fatal("Error in sentence.\n"); } #define TEST_SENTENCE(X) test_sentence(X) #else #define TEST_SENTENCE(X) #endif void free_sentences(struct sentences *s) { int e; for(e=0;e<s->slots;e++) if(s->sent[e].match) free_sentence(s,e); free((char *)s); } struct sentences *cleanup_sentences(struct sentences *s) { int e; struct sentences *s2; int size; TEST_SENTENCE(s); if(s->used_slots*3>s->slots || s->slots<=6) return s; size=s->slots/2; tot_alloc_sentence-=s->slots-size; s2=(struct sentences *)malloc(sizeof(struct sentences)+ sizeof(struct sentence)*(size-1)); s2->slots=size; s2->used_slots=0; for(e=0;e<s->slots;e++) { if(!s->sent[e].match) continue; s2->sent[s2->used_slots]=s->sent[e]; s2->used_slots++; } for(e=s2->used_slots;e<s2->slots;e++) s2->sent[e].match=NULL; free((char *)s); TEST_SENTENCE(s2); return s2; } struct sentences *remove_sent_for(struct sentences *s,struct object *o) { int e; TEST_SENTENCE(s); for(e=0;e<s->slots;e++) { if(!s->sent[e].match) continue; if(!s->sent[e].function.u.ob || s->sent[e].function.u.ob==o || (s->sent[e].function.u.ob->flags & O_DESTRUCTED)) free_sentence(s,e); } return cleanup_sentences(s); } void remove_sent(struct object *ob,struct object *p) { if(!p->sentences) return; p->sentences=remove_sent_for(p->sentences,ob); } int command_modification=0; struct sentences *add_sentence(struct sentences *s, char *match, struct svalue *fun, int pri, int args) { int unused,e; command_modification++; TEST_SENTENCE(s); if(s->used_slots==s->slots) { s=(struct sentences *)realloc((char *)s,sizeof(struct sentences)+ sizeof(struct sentence)*(s->slots*2-1)); tot_alloc_sentence+=s->slots; for(e=s->slots;e<s->slots*2;e++) s->sent[e].match=NULL; s->slots*=2; } TEST_SENTENCE(s); unused=-1; for(e=0;e<s->slots;e++) { if(s->sent[e].match==NULL) { unused=e; continue; } if(s->sent[e].pri<=pri) break; } if(unused==-1) { for(unused=e+1;s->sent[unused].match;unused++); for(;unused>e;unused--) s->sent[unused]=s->sent[unused-1]; }else{ e--; for(;unused<e;unused++) s->sent[unused]=s->sent[unused+1]; } s->sent[e].pri=pri; s->sent[e].num_arg=args; s->sent[e].match=match; assign_svalue_no_free(&(s->sent[e].function),fun); s->used_slots++; TEST_SENTENCE(s); return s; } void add_action(struct svalue *s,struct svalue *fun,int priority) { struct object *ob; char *pos; int num,e; char *str; str=copy_shared_string(strptr(s)); if (current_object->flags & O_DESTRUCTED) return; ob = current_object; if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED)) return; if (ob != command_giver && ob->super != command_giver && ob->super != command_giver->super && ob != command_giver->super) error("add_action from object that was not present.\n"); for(num=0,pos=str-1;(pos=STRCHR(pos+1,'%'));num++); if(!command_giver->sentences) { tot_alloc_sentence+=6; command_giver->sentences=(struct sentences *)malloc( sizeof(struct sentences)+sizeof(struct sentence)*5); command_giver->sentences->slots=6; command_giver->sentences->used_slots=0; for(e=0;e<6;e++) command_giver->sentences->sent[e].match=NULL; } command_giver->sentences=add_sentence(command_giver->sentences, str,fun,priority,num); } /* * Find the sentence for a command from the player. * Return success status. */ int player_parser(char *cmd) { int e,d; struct sentences *se; struct sentence *s; char *p; char verb_copy[100]; char buff[1100]; struct svalue *ss,*save_sp; extern struct svalue *sp; se=command_giver->sentences; if(!se) return 0; TEST_SENTENCE(se); /* skip leading spaces */ while(*cmd==' ') cmd++; strncpy(buff,cmd,1090); if (d_flag > 1) debug_message("cmd [%s]: %s\n", command_giver->prog->name, buff); /* strip trailing spaces. */ for (p = buff + strlen(buff) - 1; p >= buff && *p==' '; p--) *p = '\0'; if (buff[0] == '\0') return 0; save_sp=sp; for (e=0;e<se->slots;e++) { struct svalue *ret; int tr; s=se->sent+e; if(!s->match) continue; if(s->function.u.ob->flags & O_DESTRUCTED) { free_sentence(se,e); continue; } while(save_sp!=sp) pop_stack(); save_sp=sp; if(s->num_arg) { /* Gotta allocate some phony lvalues to call sscanf() */ /* Puke, puke, puke */ ss=sp; for(d=0;d<s->num_arg;d++) push_zero(); push_new_shared_string(buff); /* move outside looop */ push_shared_string(copy_shared_string(s->match)); for(d=0;d<s->num_arg;d++) { extern struct lvalue *lsp; extern struct lvalue lvalues[LVALUES]; lsp++; if(lsp>=lvalues+LVALUES) error("Lvalue stack overflow.\n"); ss++; lsp->ptr.sval=ss; lsp->rttype=T_ANY; lsp->type=LVALUE_LOCAL; sp++; sp->type = T_LVALUE; sp->u.lvalue = lsp; } tr=inter_sscanf(s->num_arg+2); if(!tr) continue; pop_n_elems(s->num_arg*2+2-tr); }else{ if(strcmp(buff,s->match)) continue; tr=0; } /* * Now we have found a special sentence ! */ if (strlen(s->match) >= sizeof verb_copy) { strncpy(verb_copy, s->match, sizeof verb_copy - 1); verb_copy[sizeof verb_copy -1] = '\0'; }else{ strcpy(verb_copy, s->match); /* what did the user type anyway? */ } last_verb = verb_copy; command_modification=0; ret = apply_lambda(&s->function, tr,0); last_verb = 0; if(!ret) error("Function for '%s' not found.\n",s->match); if (ret->type == T_NUMBER && ret->u.number == 0) { if(command_modification) error("Command list changed in action that returned 0.\n"); continue; } /* this used to update wizlist if (s && command_object->user && command_giver->interactive) { command_object->user->score++; } */ while(save_sp!=sp) pop_stack(); command_modification++; return 1; } while(save_sp!=sp) pop_stack(); command_modification++; return 0; } struct vector *query_actions(struct object *foo,struct object *bar) { int num,e; struct sentences *s; struct vector *v,*r; if(!(foo->flags & O_ENABLE_COMMANDS)) return 0; if(!(s=foo->sentences)) { return allocate_array(0); } if(bar) { for(num=e=0;e<s->slots;e++) { if(!s->sent[e].match) continue; if(s->sent[e].function.u.ob==bar) num++; } }else{ num=s->used_slots; } v=allocate_array_no_init(num,0); num=0; for(num=e=0;e<s->slots;e++) { if(!s->sent[e].match) continue; if(s->sent[e].function.u.ob->flags & O_DESTRUCTED) { free_sentence(s,e); continue; } if(bar && s->sent[e].function.u.ob!=bar) continue; r=allocate_array_no_init(3,0); SET_STR(r->item+0,copy_shared_string(s->sent[e].match)); assign_svalue_no_free(r->item+1,&(s->sent[e].function)); r->item[2].type=T_NUMBER; r->item[2].subtype=NUMBER_NUMBER; r->item[2].u.number=s->sent[e].pri; v->item[num].u.vec=r; v->item[num].type=T_POINTER; num++; } return v; } /* * Take a user command and parse it. * The command can also come from a NPC. * Beware that 'str' can be modified and extended ! */ int parse_command(char *str,struct object * ob) { struct object *save = command_giver; int res; /* disallow users to issue commands containing ansi escape codes */ #ifdef NO_ANSI char *c; for (c = str; *c; c++) { if (*c == 27) { *c = ' '; /* replace ESC with ' ' */ } } #endif command_giver = ob; res = player_parser(str); command_giver = save; return(res); } /* * Execute a command for an object. Copy the command into a * new buffer, because 'parse_command()' can modify the command. * If the object is not current object, static functions will not * be executed. This will prevent forcing players to do illegal things. * * Return cost of the command executed if success (> 0). * When failure, return 0. */ int command_for_object(char *str,struct object *ob) { char buff[1000]; extern int eval_cost; int save_eval_cost = eval_cost - 1000; struct svalue *t; if (strlen(str) > (sizeof(buff) - 1)) error("Too long command.\n"); if(ob != current_object) { push_shared_string(copy_shared_string(str)); t=apply("query_valid_force",ob,1,0); if(IS_ZERO(t)) return 0; } if (ob->flags & O_DESTRUCTED) return 0; strncpy(buff, str, sizeof buff); buff[sizeof buff - 1] = '\0'; if (parse_command(buff, ob)) return eval_cost - save_eval_cost; else return 0; } /* * Remove an object. It is first moved into the destruct list, and * not really destructed until later. (see destruct2()). */ void destruct_object(struct object *ob) { struct object *super; struct object **pp; int removed; command_modification++; if (ob->flags & O_DESTRUCTED) return; remove_object_from_stack(ob); /* * check if object has an efun socket referencing it for * a callback. if so, close the efun socket. */ if (ob->flags & O_EFUN_SOCKET) { close_referencing_sockets(ob); } if (d_flag > 1) debug_message("Destruct object %s (ref %d)\n", ob->prog->name, ob->ref); super = ob->super; while(ob->contains) { struct object *obj2; obj2 = ob->contains; push_object(ob->contains); /* An error here will not leave destruct() in an inconsistent * stage. */ APPLY_MASTER_OB((void),"destruct_environment_of",1); if (obj2->super==ob) destruct_object(obj2); } set_heart_beat(ob, 0); /* * Remove us out of this current room (if any). * Remove all sentences defined by this object from all objects here. */ if (ob->super) { if (ob->super->flags & O_ENABLE_COMMANDS) remove_sent(ob, ob->super); for (pp = &ob->super->contains; *pp;) { if ((*pp)->flags & O_ENABLE_COMMANDS) remove_sent(ob, *pp); if (*pp != ob) pp = &(*pp)->next_inv; else *pp = (*pp)->next_inv; } } if(ob->sentences) free_sentences(ob->sentences); ob->sentences=NULL; /* * Now remove us out of the list of all objects. * This must be done last, because an error in the above code would * halt execution. */ removed = 0; for (pp = &obj_list; *pp; pp = &(*pp)->next_all) { if (*pp != ob) continue; *pp = (*pp)->next_all; removed = 1; break; } if (!removed) fatal("Failed to delete object.\n"); if(ob->obj_index) { remove_my_object_hash(ob); free_string(ob->obj_index); } if(ob==command_giver) command_giver=0; ob->super = 0; ob->next_inv = 0; ob->contains = 0; ob->flags &= ~O_ENABLE_COMMANDS; ob->next_all = obj_list_destruct; obj_list_destruct = ob; ob->flags |= O_DESTRUCTED; } /* * This one is called when no program is executing from the main loop. */ void destruct2(struct object *ob) { extern int tot_alloc_object_size; struct object **hb; int i; if (d_flag > 1) debug_message("Destruct-2 object %s (ref %d)\n", ob->prog->name, ob->ref); if(ob->next_heart_beat) { extern struct object *hb_list; for(hb=&hb_list;*hb!=ob;hb=&((*hb)->next_heart_beat)) if(!*hb) fatal("Dangling hb pointer\n"); *hb=ob->next_heart_beat; ob->next_heart_beat=0; } /* * We must deallocate variables here, not in 'free_object()'. * That is because one of the local variables may point to this object, * and deallocation of this pointer will also decrease the reference * count of this object. Otherwise, an object with a variable pointing * to itself, would never be freed. * Just in case the program in this object would continue to * execute, change string and object variables into the number 0. */ /* * Deallocate variables in this object. * The space of the variables are not deallocated until * the object structure is freed in free_object(). */ for (i=0; i<ob->prog->num_variables; i++) { free_short_svalue(&ob->variables[i],ob->prog->variable_names[i].rttype); } /* this might not be true, but later we don't know have the program so we won't know the number of variables */ tot_alloc_object_size -= (ob->prog->num_variables - 1) * sizeof (struct svalue); free_prog(ob->prog, 1); free_string(ob->user); free_string(ob->eff_user); ob->user=ob->eff_user; ob->prog=0; free_object(ob, "destruct_object"); } struct object *first_inventory(struct svalue *arg) { struct object *ob=0; if (arg->type == T_OBJECT) ob = arg->u.ob; if (ob == 0) error("No object to first_inventory()"); if (ob->contains == 0) return 0; return ob->contains; } /* * This will enable an object to use commands normally only * accessible by players. * Also check if the player is a wizard. Wizards must not affect the * value of the wizlist ranking. */ void enable_commands(int num) { if (current_object->flags & O_DESTRUCTED) return; if (d_flag > 1) debug_message("Enable commands %s (ref %d)\n", current_object->prog->name, current_object->ref); if (num) { current_object->flags |= O_ENABLE_COMMANDS; command_giver = current_object; } else { if(current_object->sentences) free_sentences(current_object->sentences); current_object->sentences=0; current_object->flags &= ~O_ENABLE_COMMANDS; if(current_object == command_giver) { command_giver = 0; command_modification++; } } } #define MAX_LINES 50 /* get dir stolen from MudOS */ /* * These are used by qsort in get_dir(). */ static int pstrcmp(struct svalue *p1,struct svalue *p2) { return my_strcmp(p1, p2); } static int parrcmp(struct svalue *p1,struct svalue * p2) { return my_strcmp(p1->u.vec->item, p2->u.vec->item); } static void encode_stat(struct svalue *vp,int flags,char *str,struct stat *st) { if (flags == -1) { struct vector *v = allocate_array_no_init(3,0); SET_STR(v->item,make_shared_string(str)); v->item[1].type = T_NUMBER; v->item[1].subtype = NUMBER_NUMBER; v->item[1].u.number = ((st->st_mode & S_IFDIR) ? -2 : st->st_size); v->item[2].type = T_NUMBER; v->item[2].subtype = NUMBER_NUMBER; v->item[2].u.number = st->st_mtime; vp->type = T_POINTER; vp->u.vec = v; } else { SET_STR(vp,make_shared_string(str)); } } int match_string(char *match,char * str) { int i; again: if (*str == '\0' && *match == '\0') return 1; switch(*match) { case '?': if (*str == '\0') return 0; str++; match++; goto again; case '*': match++; if (*match == '\0') return 1; for (i=0; str[i] != '\0'; i++) if (match_string(match, str+i)) return 1; return 0; case '\0': return 0; case '\\': match++; if (*match == '\0') return 0; /* Fall through ! */ default: if (*match == *str) { match++; str++; goto again; } return 0; } } /* * List files in directory. This function do same as standard list_files did, * but instead writing files right away to user this returns an array * containing those files. Actually most of code is copied from list_files() * function. * Differences with list_files: * * - file_list("/w"); returns ({ "w" }) * * - file_list("/w/"); and file_list("/w/."); return contents of directory * "/w" * * - file_list("/");, file_list("."); and file_list("/."); return contents * of directory "/" * * With second argument equal to non-zero, instead of returning an array * of strings, the function will return an array of arrays about files. * The information in each array is supplied in the order: * name of file, * last update of file, * size of file (-2 means file doesn't exist). */ #define MAX_FNAME_SIZE 255 #define MAX_PATH_LEN 1024 typedef int (*cmpfuntyp) (const void *,const void *); struct vector *get_dir(char *path,int flags) { struct vector *v; int i, count = 0; DIR *dirp; int namelen, do_match = 0; struct dirent *de; struct stat st; char *endtemp; char temppath[MAX_FNAME_SIZE + MAX_PATH_LEN + 2]; char regexp[MAX_FNAME_SIZE + MAX_PATH_LEN + 2]; char *p; if (!path) return 0; if (strlen(path)<2) { temppath[0]=path[0]?path[0]:'.'; temppath[1]='\000'; p = temppath; } else { strncpy(temppath, path, MAX_FNAME_SIZE + MAX_PATH_LEN + 2); /* * If path ends with '/' or "/." remove it */ if ((p = STRRCHR(temppath, '/')) == 0) p = temppath; if (p[0] == '/' && ((p[1] == '.' && p[2] == '\0') || p[1] == '\0')) *p = '\0'; } if (stat(temppath, &st) < 0) { if (*p == '\0') return 0; if (p != temppath) { strcpy(regexp, p + 1); *p = '\0'; } else { strcpy(regexp, p); strcpy(temppath, "."); } do_match = 1; } else if (*p != '\0' && strcmp(temppath, ".")) { if (*p == '/' && *(p + 1) != '\0') p++; v = allocate_array(1); encode_stat(&v->item[0], flags, p, &st); return v; } if ((dirp = opendir(temppath)) == 0) return 0; /* * Count files */ for (de = readdir(dirp); de; de = readdir(dirp)) { namelen = NLENGTH(de); if (!do_match && (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)) continue; if (do_match && !match_string(regexp, de->d_name)) continue; count++; if (count >= MAX_ARRAY_SIZE) break; } /* * Make array and put files on it. */ v = allocate_array(count); if (count == 0) { /* This is the easy case :-) */ closedir(dirp); return v; } rewinddir(dirp); endtemp = temppath + strlen(temppath); strcat(endtemp++, "/"); for(i = 0, de = readdir(dirp); i < count; de = readdir(dirp)) { namelen = NLENGTH(de); if (!do_match && (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)) continue; if (do_match && !match_string(regexp, de->d_name)) continue; de->d_name[namelen] = '\0'; if (flags == -1) { /* We'll have to .... sigh.... stat() the file to get some add'tl info. */ strcpy(endtemp, de->d_name); stat(temppath, &st); /* We assume it works. */ } encode_stat(&v->item[i], flags, de->d_name, &st); i++; } closedir(dirp); /* Sort the names. */ qsort((char *)v->item, count, sizeof v->item[0], (cmpfuntyp)( (flags == -1) ? parrcmp : pstrcmp )); return v; } /* Profezzorn */ void print_svalue(struct svalue *arg) { char buf[300]; #ifdef DEBUG if (!arg) fatal("Null argument to print svalue.\n"); #endif switch(arg->type) { case T_STRING: push_svalue(arg); break; case T_FLOAT: sprintf(buf,"%0.8f", arg->u.fnum); push_new_shared_string(buf); break; case T_OBJECT: sprintf(buf,"OBJ(%d)", arg->u.ob->clone_number); push_new_shared_string(buf); break; case T_NUMBER: sprintf(buf,"%d", arg->u.number); push_new_shared_string(buf); break; case T_POINTER: sprintf(buf,"ARRAY[%d]",arg->u.vec->size); push_new_shared_string(buf); break; case T_LIST: sprintf(buf,"LIST[%d]",arg->u.vec->size); push_new_shared_string(buf); break; case T_MAPPING: sprintf(buf,"MAPPING[%d]",arg->u.vec->size); push_new_shared_string(buf); break; default: push_new_shared_string("<UNKNOWN>"); } if(!command_giver) { fwrite(strptr(sp),1,my_strlen(sp),stdout); fflush(stdout); pop_stack(); return; } (void)apply_lfun(LF_CATCH_TELL,command_giver,1,0); } void do_write(struct svalue *arg) { struct object *save_command_giver = command_giver; print_svalue(arg); command_giver = save_command_giver; } struct object *find_hashed(char *name) { struct object *ob; extern struct object *lookup_my_object_hash(char *); if(batch_mode) { name=combine_path(0,name); if(!(ob=lookup_my_object_hash(name))) ob=clone_object(name,1); free(name); }else{ if(name[0]=='/') name++; if(!(ob=lookup_my_object_hash(name))) ob=clone_object(name,1); } return ob; } struct program *find_program(char *str) { struct program *p; /* Remove leading '/' if any. */ if(!batch_mode) while(str[0] == '/') str++; p = find_program2(str); if(!p) p = load_object(str,0); return p; } /* Look for a loaded object. Return 0 if non found. */ struct program *find_program2(char *str) { register struct program *p; register int length; /* Remove leading '/' if any. */ if(!batch_mode) { while(str[0] == '/') str++; /* Truncate possible .c in the object name. */ length = strlen(str); if (IS_POSTFIX(str,LPC_SUFFIX)) { /* A new writreable copy of the name is needed. */ char *p; p = (char *)alloca(strlen(str)+1); strcpy(p, str); str = p; str[length-strlen(LPC_SUFFIX)] = '\0'; } p=lookup_program_hash(str); return p; }else{ str=combine_path(0,str); length=strlen(str); if (IS_POSTFIX(str,LPC_SUFFIX)) str[length-strlen(LPC_SUFFIX)]=0; p=lookup_program_hash(str); free(str); return p; } } /* * Transfer an object. * The object has to be taken from one inventory list and added to another. * The main work is to update all command definitions, depending on what is * living or not. Note that all objects in the same inventory are affected. * * There are some boring compatibility to handle. When -o flag is specified, * several functions are called in some objects. This is dangerous, as * object might self-destruct when called. */ void move_object(struct object *item,struct object *dest) { struct object **pp, *ob, *next_ob; struct object *save_cmd = command_giver; command_modification++; /* Recursive moves are not allowed. */ for (ob = dest; ob; ob = ob->super) if (ob == item) error("Can't move object inside itself.\n"); /* * Objects must have inherited std/object if they are to be allowed to * be moved. */ if(!(item->flags & O_APPROVED) || !(dest->flags & O_APPROVED)) { error("Trying to move object where src or dest not inherit std/object\n"); return; } if (item->super) { int okey = 0; push_object(item); (void)apply_lfun(LF_EXIT, item->super, 1,0); if (item->flags & O_DESTRUCTED || dest->flags & O_DESTRUCTED) return; /* Give up */ remove_sent(item->super, item); if (item->super->flags & O_ENABLE_COMMANDS) remove_sent(item, item->super); for (pp = &item->super->contains; *pp;) { if (*pp != item) { if ((*pp)->flags & O_ENABLE_COMMANDS) remove_sent(item, *pp); if (item->flags & O_ENABLE_COMMANDS) remove_sent(*pp, item); pp = &(*pp)->next_inv; continue; } *pp = item->next_inv; okey = 1; } if (!okey) fatal("Failed to find object %s in super list of %s.\n", item->prog->name, item->super->prog->name); } item->next_inv = dest->contains; dest->contains = item; item->super = dest; /* * Setup the new commands. The order is very important, as commands * in the room should override commands defined by the room. * Beware that init() in the room may have moved 'item' ! * * The call of init() should really be done by the object itself * (except in the -o mode). It might be too slow, though :-( */ if (item->flags & O_ENABLE_COMMANDS) { command_giver = item; (void)apply_lfun(LF_INIT, dest, 0,0); if ((dest->flags & O_DESTRUCTED) || item->super != dest) { command_giver = save_cmd; /* marion */ return; } } /* * Run init of the item once for every present player, and * for the environment (which can be a player). */ for (ob = dest->contains; ob && !(item->flags & O_DESTRUCTED); ob=next_ob) { next_ob = ob->next_inv; if (ob == item) continue; if (ob->flags & O_DESTRUCTED) error("An object was destructed at call of init()\n"); if (ob->flags & O_ENABLE_COMMANDS) { command_giver = ob; (void)apply_lfun(LF_INIT, item, 0,0); if (dest != item->super) { command_giver = save_cmd; /* marion */ return; } } if (item->flags & O_DESTRUCTED) /* marion */ error("The object to be moved was destructed at call of init()\n"); if (item->flags & O_ENABLE_COMMANDS) { command_giver = item; (void)apply_lfun(LF_INIT, ob, 0,0); if (dest != item->super) { command_giver = save_cmd; /* marion */ return; } } } if (dest->flags & O_DESTRUCTED) /* marion */ error("The destination to move to was destructed at call of init()\n"); if (item->flags & O_DESTRUCTED) /* Profezzorn */ error("The moved object was destructed.\n"); if (dest->flags & O_ENABLE_COMMANDS) { command_giver = dest; (void)apply_lfun(LF_INIT, item, 0,0); } command_giver = save_cmd; } char debug_parse_buff[50]; /* Used for debugging */ /* * Hard coded commands, that will be available to all players. They can not * be redefined, so the command name should be something obscure, not likely * to be used in the game. */ int special_parse(char *buff) { #ifdef DEBUG strncpy(debug_parse_buff, buff, sizeof debug_parse_buff); debug_parse_buff[sizeof debug_parse_buff - 1] = '\0'; #endif #if defined(MALLOC_smalloc) if (strcmp(buff, "debugmalloc") == 0) { extern int debugmalloc; debugmalloc = !debugmalloc; if (debugmalloc) vtell_object("On.\n"); else vtell_object("Off.\n"); return 1; } #endif if (strcmp(buff, "status") == 0 || strcmp(buff, "status tables") == 0) { int tot, res, verbose = 0; extern char *reserved_area; extern int tot_alloc_sentence, tot_alloc_object, tot_alloc_object_size; extern int num_arrays, total_array_size; extern int total_num_prog_blocks, total_prog_block_size; if (strcmp(buff, "status tables") == 0) verbose = 1; if (reserved_area) res = RESERVED_SIZE; else res = 0; if (!verbose) { vtell_object("Sentences:\t\t\t%8d %8d\n", tot_alloc_sentence, (int)(tot_alloc_sentence * sizeof (struct sentence))); vtell_object("Objects:\t\t\t%8d %8d\n", tot_alloc_object, tot_alloc_object_size); vtell_object("Arrays:\t\t\t\t%8d %8d\n", num_arrays, total_array_size); vtell_object("Prog blocks:\t\t\t%8d %8d\n", total_num_prog_blocks, total_prog_block_size); vtell_object("Memory reserved:\t\t\t %8d\n", res); } tot = total_prog_block_size + total_array_size + tot_alloc_object_size + res; if (!verbose) { vtell_object("\t\t\t\t\t --------\n"); vtell_object("Total:\t\t\t\t\t %8d\n", tot); } return 1; } return 0; } void fatal(char *fmt, ...) { extern void debug_message_va(char *a,va_list args); va_list args; static int in_fatal = 0; va_start(args,fmt); /* Prevent double fatal. */ if (in_fatal) abort(); in_fatal = 1; (void)VFPRINTF(stderr, fmt, args); fflush(stderr); if (current_object) (void)fprintf(stderr, "Current object was %s\n", current_object->prog->name); debug_message_va(fmt, args); if (current_object) debug_message("Current object was %s\n", current_object->prog->name); debug_message("Dump of variables:\n"); (void)dump_trace(1); abort(); } int num_error = 0; /* * Error() has been "fixed" so that users can catch and throw them. * To catch them nicely, we really have to provide decent error information. * Hence, all errors that are to be caught * (error_recovery_context_exists == 2) construct a string containing * the error message, which is returned as the * thrown value. Users can throw their own error values however they choose. */ /* * This is here because throw constructs its own return value; we dont * want to replace it with the system's error string. */ void throw_error() { extern int error_recovery_context_exists; extern jmp_buf error_recovery_context; extern struct svalue catch_value; if (error_recovery_context_exists > 1) { longjmp(error_recovery_context, 1); fatal("Throw_error failed!"); } if(catch_value.type==T_STRING) { error("Throw: %s.\n",strptr(&catch_value)); } } static char emsg_buf[2000]; void va_error(char *fmt, va_list args) { extern int error_recovery_context_exists; extern jmp_buf error_recovery_context; extern struct object *current_heart_beat; extern struct svalue catch_value; extern int eval_cost,max_eval_cost; struct object *ob,*init_ob; char buf[200]; extern struct control_stack control_stack[MAX_TRACE]; #ifdef OPCPROF check_cost_for_instr(-1); #endif VSPRINTF(emsg_buf+1, fmt, args); emsg_buf[0] = '*'; /* all system errors get a * at the start */ if (error_recovery_context_exists==2) /* user catches this error */ { struct svalue v; SET_STR(&v,make_shared_string(emsg_buf)); assign_svalue(&catch_value, &v); longjmp(error_recovery_context, 1); fatal("Catch() longjump failed"); } eval_cost=0; max_eval_cost=MAX_COST; num_error++; if (num_error > 1) fatal("Too many simultaneous errors.\n"); debug_message("%s", emsg_buf+1); if (current_object) { debug_message("program: %s, object: %s line %d\n", current_prog ? current_prog->name : "", current_object->prog->name, get_line_number_if_any()); } fprintf(stderr,"%s",emsg_buf+1); if (current_object) { fprintf(stderr,"program: %s, object: %s line %d\n", current_prog ? current_prog->name : "", current_object->prog->name, get_line_number_if_any()); } ob=dump_trace(0); fflush(stderr); if (ob && ob->flags & O_DESTRUCTED) { if(command_giver) vtell_object("error when executing program in destroyed object %s\n", ob->prog->name); debug_message("error when executing program in destroyed object %s\n", ob->prog->name); } if (error_recovery_context_exists==3) /* user catches this error */ { num_error--; free_svalue(&catch_value); longjmp(error_recovery_context, 1); fatal("Catch() longjump failed"); } if(!control_stack[1].ob || control_stack[1].ob->flags & O_DESTRUCTED) { if(!current_object || current_object->flags & O_DESTRUCTED) { init_ob=NULL; }else{ init_ob=current_object; } }else{ init_ob=control_stack[1].ob; } reset_machine (0); push_new_shared_string(emsg_buf); if(current_object && !(current_object->flags & O_DESTRUCTED)) { push_object(current_object); }else{ push_zero(); } if(current_prog) { push_shared_string(copy_shared_string(current_prog->name)); }else{ push_zero(); } if(current_object) { if(current_object->obj_index) { push_new_shared_string(current_object->prog->name); }else{ sprintf(buf,"%s(%d)",current_object->prog->name, current_object->clone_number); push_new_shared_string(buf); } }else{ push_zero(); } if(init_ob) push_object(init_ob); else push_zero(); current_object=NULL; push_number(get_line_number_if_any()); APPLY_MASTER_OB((void),"handle_error",6); if (current_heart_beat) { set_heart_beat(current_heart_beat, 0); debug_message("Heart beat in %s turned off.\n", current_heart_beat->prog->name); push_object(current_heart_beat); APPLY_MASTER_OB((void),"kill_heart_beat",1); current_heart_beat = 0; } num_error--; if (error_recovery_context_exists) longjmp(error_recovery_context, 1); if(batch_mode) exit(10); abort(); } void error(char *fmt,...) { va_list args; va_start(args,fmt); va_error(fmt,args); va_end(args); } /* * Check that it is an legal path. No '..' are allowed. */ int legal_path(char *path) { char *p; if(batch_mode) return 1; if (path == NULL || STRCHR(path, ' ')) return 0; if (path[0] == '/') return 0; #ifdef MSDOS if (!valid_msdos(path)) return(0); #endif for(p = STRCHR(path, '.'); p; p = STRCHR(p+1, '.')) { if (p[1] == '.') return 0; } return 1; } /* * There is an error in a specific file. Ask the game driver to log the * message somewhere. */ void smart_log(char *error_file,int line,char * what) { char buff[500]; if(!batch_mode) { if (error_file == 0) return; if (strlen(what) + strlen(error_file) > sizeof buff - 100) what = "...[too long error message]..."; if (strlen(what) + strlen(error_file) > sizeof buff - 100) error_file = "...[too long filename]..."; sprintf(buff, "%s line %d:%s\n", error_file, line, what); push_new_shared_string(error_file); push_new_shared_string(buff); APPLY_MASTER_OB((void),"log_error", 2); } } /* * Check that a path to a file is valid for read or write. * This is done by functions in the master object. * The path is always treated as an absolute path, and is returned without * a leading '/'. * If the path was '/', then '.' is returned. * The returned string may or may not be residing inside the argument 'path', * so don't deallocate arg 'path' until the returned result is used no longer. * Otherwise, the returned path is temporarily allocated by apply(), which * means it will be dealocated at next apply(). */ char *check_valid_path(char *path,int shared_string, char *eff_user, char *call_fun, int writeflg) { struct svalue *v; if(batch_mode) /* who cares in batchmode */ { return path; }else{ if (eff_user == 0) return 0; if(shared_string) push_shared_string(copy_shared_string(path)); else push_new_shared_string(path); push_shared_string(copy_shared_string(eff_user)); push_new_shared_string(call_fun); /* fix? */ if (writeflg) { APPLY_MASTER_OB(v=,"valid_write", 3); } else{ APPLY_MASTER_OB(v=,"valid_read", 3); } if (!v || (v->type == T_NUMBER && v->u.number == 0)) return 0; if(v->type==T_STRING) path=strptr(v); if (path[0] == '/') path++; if (path[0] == '\0') path = "."; if (legal_path(path)) return path; return 0; } } char *simple_check_valid_path(struct svalue *s,char *fun,int writeflag) { return check_valid_path(strptr(s), 1, current_object->eff_user, fun,writeflag); } /* * This one is called from HUP. */ int game_is_being_shut_down; void startshutdowngame() { game_is_being_shut_down = 1; } /* * This one is called from the command "shutdown". * We don't call it directly from HUP, because it is dangerous when being * in an interrupt. */ #define DEALLOCATE_MEMORY_AT_SHUTDOWN void shutdowngame() { #ifdef ADD_CACHE_SIZE extern void free_add_cache(); #endif extern void do_gc(int); extern char *reserved_area; extern struct svalue catch_value; char *s; int e; APPLY_MASTER_OB((void),"game_shutting_down",0); #ifdef OPCPROF dump_opcode_info(); #endif do_close_database(); #ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN free_svalue(&catch_value); free_all_call_outs(); remove_all_objects(); free_object(master_ob,"shutdown"); free_string(hostname); hostname=NULL; do_gc(4711); for(e=0;e<num_lfuns;e++) free_string(lfuns[e]); free((char*)lfuns); free_simul_efun(); free_reswords(); if(reserved_area) free(reserved_area); free_memory_in_lex_at_exit(); free_svalue(&const_empty_string); if((s=simple_free_buf())) free(s); #ifdef ADD_CACHE_SIZE free_add_cache(); #endif if(d_flag) { int e; struct object *o; struct program *p; extern void dump_stralloc_strings(); extern struct program *obj_table[OTABLE_SIZE]; for(o=obj_list;o;o=o->next_all) { printf("Object not destructed: %s(%d) %d refs\n", o->prog ? o->prog->name : "<program freed>", o->clone_number, o->ref); } for(e=0;e<OTABLE_SIZE;e++) { for(p=obj_table[e];p;p=p->next_hash) { printf("Program not freed: %s %d refs\n", p->name, p->ref); } } dump_stralloc_strings(); } #endif exit(0); } /* * Credits for some of the code below goes to Free Software Foundation * Copyright (C) 1990 Free Software Foundation, Inc. * See the GNU General Public License for more details. */ #ifndef S_ISDIR #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) #endif int isdir (char *path) { struct stat stats; return stat (path, &stats) == 0 && S_ISDIR (stats.st_mode); } void strip_trailing_slashes (char *path) { int last; last = strlen (path) - 1; while (last > 0 && path[last] == '/') path[last--] = '\0'; } struct stat to_stats, from_stats; int copy (from, to) char *from, *to; { int ifd; int ofd; char buf[1024 * 8]; int len; /* Number of bytes read into `buf'. */ if (!S_ISREG (from_stats.st_mode)) { error ("cannot move `%s' across filesystems: Not a regular file\n", from); return 1; } if (unlink (to) && errno != ENOENT) { error ("cannot remove `%s'\n", to); return 1; } ifd = open (from, O_RDONLY, 0); if (ifd < 0) { error ("%s: open failed\n", from); return errno; } ofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (ofd < 0) { error ("%s: open failed\n", to); close (ifd); return 1; } #ifdef HAVE_FCHMOD if (fchmod (ofd, from_stats.st_mode & 0777)) { error ("%s: fchmod failed\n", to); close (ifd); close (ofd); unlink (to); return 1; } #endif while ((len = read (ifd, buf, sizeof (buf))) > 0) { int wrote = 0; char *bp = buf; do { wrote = write (ofd, bp, len); if (wrote < 0) { error ("%s: write failed\n", to); close (ifd); close (ofd); unlink (to); return 1; } bp += wrote; len -= wrote; } while (len > 0); } if (len < 0) { error ("%s: read failed\n", from); close (ifd); close (ofd); unlink (to); return 1; } if (close (ifd) < 0) { error ("%s: close failed", from); close (ofd); return 1; } if (close (ofd) < 0) { error ("%s: close failed", to); return 1; } #ifndef HAVE_FCHMOD if (chmod (to, from_stats.st_mode & 0777)) { error ("%s: chmod failed\n", to,0,0,0,0,0,0,0,0); return 1; } #endif return 0; } /* Move FROM onto TO. Handles cross-filesystem moves. If TO is a directory, FROM must be also. Return 0 if successful, 1 if an error occurred. */ int do_move (char *from,char * to) { if (lstat (from, &from_stats) != 0) { error ("%s: lstat failed\n", from); return 1; } if (lstat (to, &to_stats) == 0) { #ifndef MSDOS if (from_stats.st_dev == to_stats.st_dev && from_stats.st_ino == to_stats.st_ino) #else if (same_file(from,to)) #endif { error ("`%s' and `%s' are the same file", from, to); return 1; } if (S_ISDIR (to_stats.st_mode)) { error ("%s: cannot overwrite directory", to); return 1; } } else if (errno != ENOENT) { error ("%s: unknown error\n", to); return 1; } #ifdef SYSV if (isdir(from)) { char cmd_buf[100]; sprintf(cmd_buf, "/usr/lib/mv_dir %s %s", from, to); return system(cmd_buf); } else #endif /* SYSV */ if (rename (from, to) == 0) { return 0; } if (errno != EXDEV) { error ("cannot move `%s' to `%s'", from, to); return 1; } /* rename failed on cross-filesystem link. Copy the file instead. */ if (copy (from, to)) return 1; if (unlink (from)) { error ("cannot remove `%s'", from); return 1; } return 0; } char *read_file(char *file,int start,int len) { struct stat st; FILE *f; char *str,*p,*p2,*end,c; int size; if (len < 0) return 0; if (!file) return 0; f = fopen(file, "r"); if (f == 0) return 0; if (fstat(fileno(f), &st) == -1) fatal("Could not stat an open file.\n"); size = st.st_size; if (size > READ_FILE_MAX_SIZE) { if ( start || len ) size = READ_FILE_MAX_SIZE; else { fclose(f); return 0; } } if (!start) start = 1; if (!len) len = READ_FILE_MAX_SIZE; str = xalloc(size + 1); str[size] = '\0'; do { if (size > st.st_size) size = st.st_size; if (fread(str, size, 1, f) != 1) { fclose(f); free(str); return 0; } st.st_size -= size; end = str+size; for (p=str; ( p2=MEMCHR(p,'\n',end-p) ) && --start; ) p=p2+1; } while ( start > 1 ); for (p2=str; p != end; ) { c = *p++; if (!c) c=' '; *p2++=c; if ( c == '\n' ) if (!--len) break; } if ( len && st.st_size ) { size -= ( p2-str) ; if (size > st.st_size) size = st.st_size; if (fread(p2, size, 1, f) != 1) { fclose(f); free(str); return 0; } st.st_size -= size; end = p2+size; for (; p2 != end; ) { c = *p2; if (!c) *p2=' '; p2++; if ( c == '\n' ) if (!--len) break; } if ( st.st_size && len ) { /* tried to read more than READ_MAX_FILE_SIZE */ fclose(f); free(str); return 0; } } *p2='\0'; fclose(f); #if 0 /* caller immediately frees the string again, * so there's no use to make it smaller now */ if ( st.st_size > (p2-str) ) { /* can't allocate shared string when string type isn't passed to the caller */ p2=strdup(str); free(str); return p2; } #endif return str; } /* returns a shared string */ char *read_bytes(char *file,int start,int len) { struct stat st; char *str,*p; int size, f; #ifndef HAVE_UNISTD_H int lseek(); #endif if (len < 0) return 0; if(!batch_mode && len > MAX_BYTE_TRANSFER) len=MAX_BYTE_TRANSFER; if (!file) return 0; f = open(file, O_RDONLY); if (f < 0) return 0; if (fstat(f, &st) == -1) fatal("Could not stat an open file.\n"); size = st.st_size; if(start < 0) start = size + start; if (start >= size) { close(f); return 0; } if ((start+len) > size) len = (size - start); if ((size = lseek(f,start, 0)) < 0) return 0; str = xalloc(len + 1); size = read(f, str, len); close(f); if (size <= 0) { free(str); return 0; } /* * The string has to end to '\0'!!! */ str[size] = '\0'; p = make_shared_binary_string(str,size); free(str); return p; } int write_bytes(char *file,int start,struct svalue *string) { struct stat st; int size, f,len; #ifndef HAVE_UNISTD_H int lseek(); #endif char *str; str=strptr(string); len=my_strlen(string); if (!file)return 0; if(!batch_mode && len > MAX_BYTE_TRANSFER) return 0; f = open(file, O_WRONLY | O_CREAT); if (f < 0) return 0; if (fstat(f, &st) == -1) fatal("Could not stat an open file.\n"); size = st.st_size; if(start < 0) start += size; if(start < 0) return 0; if ((size = lseek(f,start, 0)) < 0) return 0; size = write(f, str, len); close(f); if (size <= 0) return 0; return size; } #include <sys/param.h> char *combine_path(char *cwd,char *file) { /* cwd is supposed to be combined already */ register char *p,*pp,*ppp,*pppp; char *my_cwd; my_cwd=0; if(file[0]=='/') { cwd="/"; file++; }else{ if(!cwd) { #ifdef HAVE_GETWD cwd=(char *)getwd(my_cwd=(char *)malloc(MAXPATHLEN+1)); if(!cwd) fatal("Couldn't fetch current path.\n"); #else #ifdef HAVE_GETCWD my_cwd=cwd=(char *)getcwd(0,1000); #else /* maybe autoconf was wrong.... if not, insert your method here */ my_cwd=cwd=(char *)getcwd(0,1000); #endif #endif } } if(cwd[strlen(cwd)-1]=='/') { p=(char *)xalloc(strlen(cwd)+strlen(file)+1); strcpy(p,cwd); strcat(p,file); }else{ p=(char *)xalloc(strlen(cwd)+strlen(file)+2); strcpy(p,cwd); strcat(p,"/"); strcat(p,file); } for(pp=STRCHR(p,'/');pp && *pp;) { if(pp[1]=='.') { if(pp[2]=='.' && (pp[3]=='/' || pp[3]==0)) { pppp=pp+3; for(ppp=pp-1;ppp>=p && *ppp!='/';ppp--); pp=ppp; /* note: assignment, not comparisment */ while((*(ppp++)=*(pppp++))); continue; }else if(pp[2]=='/' || pp[2]==0){ ppp=pp; pppp=pp+2; while((*(ppp++)=*(pppp++))); /* note: assignment, not comparisment */ continue; } } pp=STRCHR(pp+1,'/'); } if(my_cwd) free(my_cwd); return p; }