Index: TODO ================================================================== --- TODO +++ TODO @@ -30,12 +30,9 @@ * Bugs * Figure out why the $SeekerCloser class doesn't seem to work properly * Level 232 of SUPERHRO puzzle set (the Lava shouldn't expand? why?) * Display solution comments/timestamp * VCR mode -* Portable mode, not needing installing files in home directory - * Maybe not needed? You can override the HOME environment variable - * An alternative is to check argv[0]; if it contains / then don't use HOME * Command-line switch for batch import/export levels * SQL * Implement the GROUP column in the CLASSES table * Allow multiple SQL statements in one binding Index: commandline.doc ================================================================== --- commandline.doc +++ commandline.doc @@ -38,10 +38,14 @@ Start in the editor instead of game. -f Only flush the user cache and then terminate. +-h + Disable portable mode; always use the HOME environment variable to find + the configuration and database files. + -n Create a new puzzle set. The .xclass and .class files should already exist; the .level and .solution files should not already exist, and will be created with minimal data. @@ -87,5 +91,24 @@ Disable loading SQLite extensions. .x1 Enable loading SQLite extensions. + +=== Portable mode === + +Free Hero Mesh may run either in "home mode" or in "portable mode". The +following rules are used to determine which mode to use: + +1. If the -h switch is present, use home mode. + +2. If the HEROMESH_PREFIX environment variable is present, use portable +mode; its value is the file name prefix (the ".heromeshsession" and +".heromeshrc" names are appended directly to this value, so if it does +not end with a slash, it will be a prefix to the file name too). + +3. If argv[0] contains a forward slash, use portable mode; it will use +files "current.heromeshrc" and "current.heromeshsession" in the directory +where the executable file is allegedly found. + +4. Otherwise, home mode is used. + Index: main.c ================================================================== --- main.c +++ main.c @@ -65,10 +65,11 @@ static SDL_Cursor*cursor[77]; static FILE*levelfp; static FILE*solutionfp; static sqlite3_int64 leveluc,solutionuc; static sqlite3_stmt*readusercachest; +static char*hpath; static sqlite3_int64 reset_usercache(FILE*fp,const char*nam,struct stat*stats,const char*suffix) { sqlite3_stmt*st; sqlite3_int64 t,id; char buf[128]; @@ -543,19 +544,12 @@ v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:""; sqlite3_config(SQLITE_CONFIG_COVERING_INDEX_SCAN,(int)boolxrm(v,1)); if(sqlite3_initialize()) fatal("Failure to initialize SQLite.\n"); optionquery[1]=Q_sqlFile; v=xrm_get_resource(resourcedb,optionquery,optionquery,2); - if(v && *v) { - s=sqlite3_mprintf("%s",v); - } else { - v=getenv("HOME")?:"."; - s=sqlite3_mprintf("%s%s.heromeshsession",v,v[strlen(v)-1]=='/'?"":"/"); - } - if(!s) fatal("Allocation failed\n"); - if(z=sqlite3_open(s,&userdb)) fatal("Failed to open user database %s (%s)\n",s,userdb?sqlite3_errmsg(userdb):sqlite3_errstr(z)); - sqlite3_free(s); + if(!v || !*v) strcpy(hpath+strlen(hpath)-2,"session"),v=hpath; + if(z=sqlite3_open(v,&userdb)) fatal("Failed to open user database %s (%s)\n",v,userdb?sqlite3_errmsg(userdb):sqlite3_errstr(z)); optionquery[1]=Q_sqlExtensions; v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:""; sqlite3_db_config(userdb,SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,*v?1:0,&z); if(*v) { p=s=strdup(v); @@ -588,19 +582,37 @@ void set_cursor(int id) { id>>=1; if(!cursor[id]) cursor[id]=SDL_CreateCursor((void*)cursorimg+(id<<6),(void*)cursorimg+(id<<6)+32,16,16,cursorhot[id]>>4,cursorhot[id]&15); SDL_SetCursor(cursor[id]); } + +static void set_path(const char*arg) { + const char*s; + if(main_options['h']) goto home; + if((s=getenv("HEROMESH_PREFIX")) && *s) { + hpath=malloc(strlen(s)+32); + if(!hpath) fatal("Allocation failed\n"); + sprintf(hpath,"%s.heromeshrc",s); + return; + } + if(s=strrchr(arg,'/')) { + hpath=malloc(s+64-arg); + if(!hpath) fatal("Allocation failed\n"); + sprintf(hpath,"%.*s/current.heromeshrc",(int)(s-arg),arg); + return; + } + home: + s=getenv("HOME")?:"."; + hpath=malloc(strlen(s)+32); + if(!hpath) fatal("Allocation failed\n"); + sprintf(hpath,"%s%s.heromeshrc",s,s[strlen(s)-1]=='/'?"":"/"); +} static void load_options(void) { - const char*home=getenv("HOME")?:"."; - char*nam=malloc(strlen(home)+16); FILE*fp; - sprintf(nam,"%s%s.heromeshrc",home,home[strlen(home)-1]=='/'?"":"/"); - fp=fopen(nam,"r"); - if(!fp) fatal("Failed to open %s (%m)\n",nam); - free(nam); + fp=fopen(hpath,"r"); + if(!fp) fatal("Failed to open %s (%m)\n",hpath); if(xrm_load(resourcedb,fp,1)) fatal("Error while loading .heromeshrc\n"); fclose(fp); } static void read_options(int argc,char**argv) { @@ -889,11 +901,14 @@ main_options['x']=1; } if(main_options['a']) main_options['r']=main_options['x']=1; if(main_options['p']) main_options['r']=1; if(main_options['f']) main_options['x']=1; - if(!main_options['c']) load_options(); + if(!main_options['c']) { + set_path(argv[0]); + load_options(); + } if(argc>optind) read_options(argc-optind,argv+optind); *optionquery=xrm_make_quark(globalclassname,0)?:xrm_anyq; #ifdef __GNUC__ stack_protect_mark=__builtin_frame_address(0); set_stack_protection(); @@ -909,10 +924,11 @@ run_picture_editor(); return 0; } load_pictures(); if(main_options['T']) { + printf("argv[0] = %s\n",argv[0]); test_mode(); return 0; } init_usercache(); if(main_options['n']) return 0; Index: man6/heromesh.6 ================================================================== --- man6/heromesh.6 +++ man6/heromesh.6 @@ -29,10 +29,13 @@ You can also use it with some other options to display details. .IP -e Start the level editor. .IP -f Only flush the user cache and then terminate. +.IP -h +Disable portable mode; +always use the HOME environment variable to find the files. .IP -n Create a new puzzle set. The class definition file and image set must already exist. .IP -p Start the picture editor. @@ -54,11 +57,30 @@ .I ~/.heromeshsession A SQLite database containing user cache data. .TP .I \*[Document_Dir]/*.doc Documentation. +.PP +The files it expects to find in the home directory may be looked for elsewhere if portable mode is used. .SH ENVIRONMENT .TP +.B HEROMESH_PREFIX +If defined, force portable mode. +This value is used as the prefix to find the files. +(It may include a file name prefix as well as a directory path.) +.TP .B HOME The home directory; used to find .heromeshrc and .heromeshsession files. .PP Any of the SDL environment variables can also be used. +.SH "PORTABLE MODE" +There is a "portable mode" available. +The following rules are used to determine if portable mode should be used: +.PP +1. If the -h switch is specified, use home mode. +.PP +2. If the HEROMESH_PREFIX environment variable is nonempty, use portable mode. +.PP +3. If argv[0] contains a forward slash, use portable mode. +In this case, the files are named "current.heromeshrc" and "current.heromeshsession" in the directory where the executable file is found. +.PP +4. Otherwise, use home mode.