/* Copyright (c) Günter Woigk 2001-2009 mailto:kio@little-bat.de This file is free software This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2003-08-15 kio fixed stdin/out/err redirection */ #define SAFE 3 #define LOG 1 #include "config.h" #include #include #include #include #include #include //#include "unix/file_utilities.h" #include "unix/os_utilities.h" //#include "ustring/sstring.h" #define _include_version_as_source_ #include "version.h" #include "token.h" #include "globals.h" //#include "var/NameHandles.h" #include "Stream.h" #include "VScript.h" #include "Thread.h" XXXInitMsg(""); cstr appl_name = applName; // for abort.cp //Stream* stdStream[3] = { NULL,NULL,NULL }; static struct termios old_stdin_termios; bool verbose = 0; /* ---- restore old settings for stdin --------------------------- sheduled via atexit() */ static void restore_stdin ( void ) { XXLogIn("RestoreStdin()"); char bu[]="\033[0m"; write(0,bu,4); // reset text attributes tcsetattr(0,TCSADRAIN,&old_stdin_termios); // drain output, then change } /* ---- show help summary ----------------------------------- display optimized for 80 character terminals */ static void ShowHelp ( void ) { fputs( "\n" "______________________________________________________________________________\n" "" applLongName " " applVersion " " applCopyright ".\n" " released by " releaseUser "@" releaseHost " at " releaseDate ".\n" " built by " buildUser "@" buildHost " at " buildDate " for " buildTarget ".\n" " send bug reports to: " applEmail "\n" " development release. please set your terminal to utf-8 and vt100.\n" " see for latest documentation.\n" "\n" "vipsi -v => verbose mode\n" "vipsi -p => pretend - check syntax only\n" "vipsi -h => print help screen\n" "vipsi -t => run internal test\n" "vipsi -i file => set stdin\n" "vipsi -o file => set stdout\n" "vipsi -l file => set stderr\n" "\n" "vipsi filename => execute script.\n" "vipsi => interactive shell. help available from within.\n" "______________________________________________________________________________\n" "", stderr ); } /* ---- program entry --------------------------- */ int main( int argc, cstr argv[] ) { int i=0; XXLogIn("main()"); XXTRAP(argc==0); XXTRAP(argv==NULL||argv[0]==NULL); if (XXSAFE) { for (int i=0;i<8;i++) { static uchar bu[16]; write_double(bu+i,123.456e78); TRAP(read_double(bu+i)!=123.456e78); } } #ifdef _MACOSX cstr path = getenv("PATH"); if( FindStr(path,"/sw/")==NULL ) setenv( "PATH", CatStr(path,":/sw/bin:/sw/sbin"), yes ); if( FindStr(path,"/opt/local/")==NULL ) setenv( "PATH", CatStr(path,":/opt/local/bin:/opt/local/sbin"), yes ); #endif cstr stdinName = NULL; cstr stdoutName = NULL; cstr stderrName = NULL; cstr argvName = argv[i++]; cstr argvFile = NULL; bool pretend = no; verbose = no; bool test = no; bool shell = no; bool cgi = no; srandom(CurrentTime()); ClearError(); // scan for options while ( i=argc||stdinName ) goto h; stdinName = FullPath(argv[i++],1); if(Error()) goto x; int newfd = open(stdinName,O_RDONLY); if(newfd==-1) goto x; // errno set if(close(0/*stdin*/)==-1) goto x; if(dup(newfd)!=0) { SetError("dup(fd(0)) failed"); goto x; } close(newfd); } continue; case 'o': { if( i>=argc||stdoutName ) goto h; stdoutName = FullPath(argv[i++],1); if(Error()) goto x; int newfd = open(stdoutName,O_WRONLY|O_CREAT|O_TRUNC,0660/*muss angegeben werden!*/); if(newfd==-1) goto x; // errno set ClearError(); // errno=ENOENT if file did not exist! if(close(1/*stdout*/)==-1) goto x; if(dup(newfd)!=1) { SetError("dup(fd(1)) failed"); goto x; } close(newfd); } continue; case 'l': { if( i>=argc||stderrName ) goto h; stderrName = FullPath(argv[i++],1); if(Error()) goto x; int newfd = open(stderrName,O_WRONLY|O_CREAT|O_TRUNC,0660/*muss angegeben werden!*/); if(newfd==-1) goto x; // errno set ClearError(); // errno=ENOENT if file did not exist! if(close(2/*stderr*/)==-1) goto x; if(dup(newfd)!=2) { SetError("dup(fd(2)) failed"); /* printing errorstr will probably fail... */ goto x; } close(newfd); } continue; default: h: ShowHelp(); // argv[] error => print help text const uint commandlineerror = 125; return commandlineerror; // and return error code, don't print error message } } } if(Error()) { x: Log ( "\n%s: %s\n\n", argvName, ErrorStr() ); return Error(); } if (verbose) { Log("\n"); Log( "Compiled with %s\n", compiler); Log( "Compiled for %s\n", platform); Log( "Running on a %s CPU\n", processor); Log( "Byte order is %s\n", endian); Log("\n"); Log( "Memory alignment is %s\n", _ALIGNMENT_REQUIRED ? "required" : "not required" ); Log( "max. alignment is %i\n", _MAX_ALIGNMENT); Log( "%s alignments:\n", _ALIGNMENT_REQUIRED ? "Required" : "Preferred" ); Log( " short ....... %i\n", _SHORT_ALIGNMENT); Log( " int ......... %i\n", _INT_ALIGNMENT); Log( " long ........ %i\n", _LONG_ALIGNMENT); Log( " llong ....... %i\n", _LONG_LONG_ALIGNMENT); Log( " double ...... %i\n", _DOUBLE_ALIGNMENT); Log( " pointer ..... %i\n", _POINTER_ALIGNMENT); Log( "sizeof(size_t) = %i\n", sizeof(size_t)); Log( "sizeof(float) = %i\n", sizeof(float)); Log( "sizeof(double) = %i\n\n", sizeof(double)); if(sizeof(Double)!=sizeof(double)) Log( "sizeof(Double) = %i\n\n", sizeof(Double)); // info about current environment: Log("\n"); Log("argvName = %s\n", argvName ); Log("stdin = %s\n", stdinName ); Log("stdout = %s\n", stdoutName); Log("stderr = %s\n", stderrName); for (int j=0;j<3;j++) { if (ClassifyFile(j)!=s_tty) continue; int rows,cols; errno=ok; if( TerminalSize ( j, rows, cols ) == -1 ) Log("TerminalSize() failed: %s", ErrorStr()); else Log( "Terminal size: rows,cols = %u,%u\n",rows,cols ); break; } Log("\n"); TRAP(Error()); } // run internal test ? if (verbose&&test) { #ifdef INCLUDE_VAR_TEST_SUITE ClearError(); TestStringClass(); if (Error()) Log("*** String Test error: %s ***\n\n",ErrorStr()); else LogNL(); #else LogLine("class Var test suite was not compiled in.\n"); #endif #ifdef INCLUDE_STRING_TEST_SUITE ClearError(); TestVarClass(); if (Error()) Log("*** Var Test error: %s ***\n\n",ErrorStr()); else LogNL(); #else LogLine("class String test suite was not compiled in.\n"); #endif ClearError(); } // setup tty if (ClassifyFile(0)==s_tty) { struct termios new_stdin_termios; if( tcgetattr(0,&old_stdin_termios) != -1 && // for restore tcgetattr(0,&new_stdin_termios) != -1 ) // for vipsi { TRAP(Error()); atexit(restore_stdin); // ***TODO*** also on error exit TRAP(Error()); new_stdin_termios.c_lflag &= ~ICANON; // noncanonical input: no wait for nl, no erase, no kill processing new_stdin_termios.c_lflag &= ~ECHO; // no echo on input new_stdin_termios.c_lflag &= ~ECHONL; // also no echo for nl on input new_stdin_termios.c_cc[VMIN] = 1; // min. input bytes for read(); note: 0 does not work with FILE* !! new_stdin_termios.c_cc[VTIME] = 0/*1*/; // max. wait time in 0.1s for read(); 0==off TRAP( tcsetattr(0,TCSADRAIN,&new_stdin_termios) ); // drain output, then change SetBlocking(0,yes); TRAP(Error()); } } // run test script ? if (test) // vipsi -t { cstr fname = "00_test.vs"; cstr pp[] = { "../libs/test_suite/", // libs in installation directory "../../libs/test_suite/", // libs in installation directory (MacOS) "../../../libs/test_suite/", // libs in installation directory (MacOS) "/usr/lib/vipsi/test_suite/", // system-wide libs "~/.vipsi/test_suite/", // libs in user preferences }; cstr p = NULL; time_t t = 0; for(uint j=0;jt) { t = tt; p=pp[j]; } } ClearError(); if (!p) { LogLine("file \"test_suite/00_test.vs\" not found."); return 1; } else { argvFile = NewCopy(CatStr(p,fname)); // durable copy; memleak ignored LogLine("Loading test script \"%s\"",argvFile); chdir(p); } } // shell mode ? else if (i==argc) { shell=yes; cstr fname = "shell.vs"; cstr pp[] = { "libs/", // libs in project directory "../libs/", // libs in installation directory "../../libs/", // libs in installation directory (Mac OSX) "../../../libs/", // libs in installation directory (Mac OSX) "/usr/lib/vipsi/", // system-wide libs "~/.vipsi/", // libs in user preferences }; cstr p = NULL; for(uint j=0;jLock(); } cgi = vscript.RunType()==rt_cgi; } // result: // bash: 126 = command found but not executable // 127 = command not found // 128+N command terminated by signal N // all other codes possible too! const uint errno_notrepresentable = 125; const uint result_notrepresentable = 125; uint result = Error() ? (uint)Error() < errno_notrepresentable ? (uint)Error() : errno_notrepresentable : v==NULL ? 0 : v->IsNumber() && (uint)v->Value()Value() : result_notrepresentable; // error ? if(Error()) { cstr errstr = DupStr(ErrorStr()); ClearError(); if(cgi) { str serverAdmin = getenv("SERVER_ADMIN"); if ( !serverAdmin||strchr(serverAdmin,'@')==NULL ) { serverAdmin = "please inform someone who can help."; } else { serverAdmin = CatStr( "please inform the system administrator ", serverAdmin, "" ); } printf( "content-type: text/html; charset=utf-8\n\n" "\n" "vipsi error message\n" "\n" "" "
" "

" applLongName " version " applVersion "
" applCopyright "
vipsi - shell and script language: " "vipsi.sourceforge.net
\n" "

An error occured while processing the script" "
%s:\n" "

%s\n" "

Maybe the owner of this site is moving things around." "
If this problem persists over a prolongued time, e.g. more than an hour, " "
%s" "

\n", ToHtml(Quoted((argvFile))), ToHtml(errstr), serverAdmin ); } else { LogNL(); Log( CString( VT_FGColor(CatStr("while executing script ",Quoted(argvFile),":"),VT_red) ) ); LogNL(); Log( CString( VT_FGColor(errstr,VT_red) ) ); LogNL(); } } // stats: if (verbose) { #ifdef USE_KIOS_MALLOC #ifdef MALLOC_STATISTICS LogLine(" internal memory requests: %li total", malloc_requests ); LogLine(" external memory allocations: %li total", malloc_xrequests ); LogLine(" internal memory still in use: %li", MallocUsedBlocks() ); #endif #endif #ifdef OPCODE_PROFILING LogLine("\ntokens defined: %i",(int)tokens); LogLine("\nopcode singles:"); for (int i=0; i<40; i++) { ulong m=0; for (uint j=0;jopcodeProfile[m]) m=j; } LogLine ( " %s%8lu", (TokenName(m)+" ").LeftString(12).CString(), opcodeProfile[m] ); opcodeProfile[m]=0; } LogLine("\nopcode tupels:"); for (int i=0; i<40; i++) { ulong m=0,n=0; for (uint j=0;jopcodeTupels[m][n]) { m=j; n=k; } } LogLine ( " %s%s%8lu", (TokenName(m)+" ").LeftString(12).CString(), (TokenName(n)+" ").LeftString(12).CString(), opcodeTupels[m][n] ); opcodeTupels[m][n]=0; } #endif } XXLogLine("/*main*/"); return result; }