/****************************************************************************** * * ELMER, A Computational Fluid Dynamics Program. * * Copyright 1st April 1995 - , Center for Scientific Computing, * Finland. * * All rights reserved. No part of this program may be used, * reproduced or transmitted in any form or by any means * without the written permission of CSC. * ******************************************************************************/ /******************************************************************************* * * MATC main module. * ******************************************************************************* * * Author: Juha Ruokolainen * * Address: Center for Scientific Computing * Tietotie 6, P.O. BOX 405 * 02101 Espoo, Finland * Tel. +358 0 457 2723 * Telefax: +358 0 457 2302 * EMail: Juha.Ruokolainen@csc.fi * * Date: 30 May 1996 * * Modified by: * * Date of modification: * ******************************************************************************/ /*********************************************************************** | | MATC - Last Edited 9. 8. 1988 | ***********************************************************************/ /*====================================================================== |Syntax of the manual pages: | |FUNCTION NAME(...) params ... | $ usage of the function and type of the parameters ? explane the effects of the function = return value and the type of value if not of type int @ globals effected directly by this routine ! current known bugs or limitations & functions called by this function ~ these functions may interest you as an alternative function or | because they control this function somehow ^=====================================================================*/ /* * $Id: matc.c,v 1.7 2007/06/08 08:12:17 jpr Exp $ * * $Log: matc.c,v $ * Revision 1.7 2007/06/08 08:12:17 jpr * *** empty log message *** * * Revision 1.6 2006/02/07 10:21:42 jpr * Changed visibility of some variables to local scope. * * Revision 1.5 2006/02/02 06:54:44 jpr * small formatting changes. * * Revision 1.3 2005/08/25 13:44:22 vierinen * windoze stuff * * Revision 1.2 2005/05/27 12:26:20 vierinen * changed header install location * * Revision 1.1.1.1 2005/04/14 13:29:14 vierinen * initial matc automake package * * Revision 1.2 1998/08/01 12:34:48 jpr * * Added Id, started Log. * * */ #define MODULE_MATC #include "elmer/matc.h" #include "str.h" #include "../config.h" #ifdef DEBUG static FILE *fplog; static int tot; #endif /*====================================================================== ? main program, initialize few constants and go for it. ^=====================================================================*/ void mtc_init( FILE *input_file, FILE *output_file, FILE *error_file ) { VARIABLE *ptr; char str[256]; int i; /* i'm getting tired with all these i's */ static char *evalHelp = { "eval( str )\n\n" "Evaluate content variable. Another form of this command is @str.\n" }; static char *sourceHelp = { "source( name )\n\n" "Execute commands from file given name.\n" }; static char *helpHelp = { "help or help(\"symbol\")\n\n" "First form of the command gives list of available commands.\n" "Second form gives help on specific routine.\n" }; #ifdef DEBUG fplog = fopen("matcdbg","w"); #endif ALLOC_HEAD = (LIST *)NULL; /* * input & output & error streams */ math_in = input_file; math_err = error_file; math_out = output_file; mtr_com_init(); /* initialize matrix handling commands */ var_com_init(); /* "" VARIABLE "" "" */ fnc_com_init(); /* "" function handling commands */ fil_com_init(); /* "" file handling commands */ gra_com_init(); /* "" graphics commands */ str_com_init(); /* "" string handling */ /* * and few others. */ com_init( "eval" , FALSE, FALSE, com_apply, 1, 1, evalHelp ); com_init( "source" , FALSE, FALSE, com_source, 1, 1, sourceHelp ); com_init( "help" , FALSE, FALSE, com_help , 0, 1, helpHelp ); com_init( "quit" , FALSE, FALSE, com_quit , 0, 0, "quit\n" ); com_init( "exit" , FALSE, FALSE, com_quit , 0, 0, "exit\n" ); /* * these constants will always be there for you. */ ptr = const_new("true", TYPE_DOUBLE, 1, 1); M(ptr,0,0) = 1.0; ptr = const_new("false", TYPE_DOUBLE, 1, 1); M(ptr,0,0) = 0.0; ptr = const_new("stdin", TYPE_DOUBLE, 1, 1); M(ptr,0,0) = 0; ptr = const_new("stdout", TYPE_DOUBLE, 1, 1); M(ptr,0,0) = 1; ptr = const_new("stderr", TYPE_DOUBLE, 1, 1); M(ptr,0,0) = 2; ptr = const_new("pi", TYPE_DOUBLE, 1, 1); M(ptr,0,0) = 2*acos(0.0); #if 0 /* * trap INTERRUPT and Floating Point Exeption signals */ signal(SIGFPE, sig_trap); sprintf( str, "%s/lib/mc.ini", getenv("ELMER_POST_HOME") ); if ( (math_in = fopen( str, "r" ) ) != (FILE *)NULL) { doread(); fclose( math_in ); } /* * and finally standard input. */ math_in = stdin; doread(); var_free(); com_free(); fnc_free(); const_free(); mem_free_all(); #ifdef DEBUG fclose(fplog); #endif #endif return; /* done */ } char * mtc_domath( char *str ) { VARIABLE *headsave; /* this should not be here */ jmp_buf jmp, *savejmp; /* save program context */ void (*sigfunc)() = (void (*)())signal( SIGINT, sig_trap ); if ( !str || !*str ) { str = (char *)doread(); signal( SIGINT, sigfunc ); return math_out_str; } savejmp = jmpbuf; jmpbuf = &jmp; #ifdef DEBUG fprintf( stderr, "got [%s]\n", str ); #endif if ( math_out_str ) math_out_str[0] = '\0'; math_out_count = 0; /* * try it */ if (*str != '\0') { ALLOC_HEAD = (LIST *)NULL; headsave = (VARIABLE *)VAR_HEAD; /* * normal return takes branch 1, * error() takes branch 2, * quit() takes branch 3. */ switch (setjmp(*jmpbuf)) { case 0: (void)doit( str ); longjmp(*jmpbuf, 1); break; case 1: break; case 2: VAR_HEAD = (LIST *)headsave; break; case 3: break; } } jmpbuf = savejmp; signal( SIGINT, sigfunc ); return math_out_str; } char *doread() /*====================================================================== ? doread() is really the main loop of this program. Function reads | it's input as strings and gives them to function doit() for | execution. setjmp() function is used for error recovery. | | Memory allocated during the lifetime of this function is | collected to a list represented by the global VARIABLE | ALLOCLIST *alloc_list. If function error() is called, this | list is used to deallocte memory. Normally (well I certainly | hope so) functions which allocate memory deallocate it themselves. | | Program stays in this function until an end of file -condition | is reached or exit- or quit-commands are given. | @ jmp_buf *jmpbuf, ALLOC_LIST *alloc_list & ALLOCMEM, FREEMEM, setjmp(), longjmp() ~ doit(), quit(), error() ^=====================================================================*/ { VARIABLE *headsave; /* this should not be here */ jmp_buf jmp, *savejmp; /* save program context */ char *p, *q; /* buffer for input stream */ savejmp = jmpbuf; jmpbuf = &jmp; if ( math_out_str ) math_out_str[0] = '\0'; math_out_count = 0; p = q = ALLOCMEM(4096); /* * try it */ while(dogets(p, PMODE_MAIN)) { if (*p != '\0') { ALLOC_HEAD = (LIST *)NULL; headsave = (VARIABLE *)VAR_HEAD; /* * normal return takes branch 1, * error() takes branch 2, * quit() takes branch 3. */ switch (setjmp(*jmpbuf)) { case 0: (void)doit(p); longjmp(*jmpbuf, 1); break; case 1: break; case 2: VAR_HEAD = (LIST *)headsave; break; case 3: goto ret; break; } } } ret: jmpbuf = savejmp; FREEMEM(q); return math_out_str; } VARIABLE *com_quit() /*====================================================================== ? Quit current doread entry by longjumping back to it (nasty). & longjmp ~ doread ^=====================================================================*/ { longjmp(*jmpbuf, 3); return (VARIABLE *)NULL; /* won't be executed (hopefully) */ } int dogets(buff, prompt) char *buff; char *prompt; /*====================================================================== ? Get line from input stream. If both input & output streams are | connected to terminal, this function gives user one of three | (default) prompts: | | MATC> | - normal prompt (PMODE_MAIN) | ....> | - begin end- block is beign defined (PMODE_BLOCK) | ####> (PMODE_CONT) | - user has given a #-sign as a last character of | previous line, this line will be catenated with it | | If current comment character is found from input stream, the | line after this character is discarded. Likewise if current | system command character is found, the rest of the line is | passed to system()-call. | = line got -> TRUE, EOF -> FALSE ! There should be a way to get an echo when reading from file. & fprintf(), isatty(), fileno(), strlen(), fgets(), system() ^=====================================================================*/ { char *ptr = buff, *p; /* Can't get rid of these. */ if ( !math_in ) return FALSE; /* Try figuring out if input & output streams are terminals, if they both are, give user a prompt. */ if (isatty(fileno(math_in)) && isatty(fileno(math_out))) PrintOut( "%s", prompt ); /* i'm not in the mood to explain this. */ *ptr++ = ' '; /* Go for it. */ while((ptr = fgets(ptr, 256, math_in)) != NULL) { ptr[strlen(ptr)-1] = '\0'; /* * Check if the user wants to continue with this line. */ while(ptr[strlen(ptr)-1] == '\\') { ptr += strlen(ptr) - 1; dogets(ptr, PMODE_CONT); } /* * if there is only spaces in this line, * don't bother returning it, instead * let's read afresh, otherwise return. */ p = ptr; while(isspace(*p)) p++; if (*p != '\0') /* GOOD EXIT HERE */ { #if 0 /* * Look for the system character, if found * pass rest of the line to system()-call */ for(p = buff; *p; p++) { switch(*p) { case SYSTEM: system(p + 1); PrintOut("\n"); *p = '\0'; p--; break; } } #endif if (*buff != '\0') return TRUE; /* OR IF WE ARE HONEST, IT'S HERE */ } /* if it's terminal give a prompt. */ if (isatty(fileno(math_in)) && isatty(fileno(math_out))) PrintOut("%s", prompt); } return FALSE; } void com_init(word, flag_pw, flag_ce, sub, minp, maxp, help_text ) /*====================================================================== ? Adds commands to global command list. | | Parameters: | char *word | - the keyword user gives for this command to be executed. | int flag_pw | - flag telling if the command can be executed element | by element using function *(*sub)(). | int flag_ce | - flag telling if the command can be executed when | preprosessing if constant arguments | double *(*sub)() | - function to be executed when this command is given | int minp, maxp | - maximum and minimum number of parameters to command | | The global list of available commands is updated (or created if | nonexistent). | & lst_add() ~ *_com_init() ^=====================================================================*/ char *word; VARIABLE *(*sub)(); int minp, maxp, flag_pw, flag_ce; char *help_text; { COMMAND *ptr; /* can't get rid of this */ /* Fill the structure... */ ptr = (COMMAND *)ALLOCMEM(COMSIZE); NAME(ptr) = STRCOPY(word); if (flag_pw) ptr->flags |= CMDFLAG_PW; if (flag_ce) ptr->flags |= CMDFLAG_CE; ptr->minp = minp; ptr->maxp = maxp; ptr->sub = sub; ptr->help = help_text; /* ...and update the list. */ lst_add(COMMANDS, (LIST *)ptr); return; } void com_free() /*====================================================================== ? Deletes the list of commands and frees associated memory. | & lst_purge() ^=====================================================================*/ { /* Give memory back to system */ lst_purge(COMMANDS); return; } COMMAND *com_check(str) char *str; /*====================================================================== ? Look for command from COMMANDS list by name. | = COMMAND *NULL if does not exist, pointer to command otherwise & lst_find() ^=====================================================================*/ { return (COMMAND *)lst_find(COMMANDS, str); } VARIABLE *com_help( VARIABLE *ptr ) /*====================================================================== ? Print list of commands and user defined functions from global lists. | ! The command to get here is "help" but it really is not very helpful. | & lst_print() ^=====================================================================*/ { COMMAND *cmd; FUNCTION *fnc; char *name; if ( !ptr ) { lst_print(COMMANDS); lst_print(FUNCTIONS); } else { name = var_to_string( ptr ); if ( (cmd = com_check( name ) ) != (COMMAND *)NULL ) { if ( cmd->help ) PrintOut( "\n%s\n", cmd->help ); else PrintOut( "\nSorry: no help available on [%s].\n", name ); } else if ( (fnc = fnc_check( name ) ) != (FUNCTION *)NULL ) { if ( fnc->help ) PrintOut( "\n%s", fnc->help ); else PrintOut( "\nSorry: no help available on [%s].\n", name ); } else { error( "help: symbol not found: [%s]\n", name ); } FREEMEM( name ); } return (VARIABLE *)NULL; } VARIABLE *com_pointw(sub, ptr) double (*sub)(); VARIABLE *ptr; /*====================================================================== ? This routine does a function call (*sub)(), for each element in | matrix given by ptr. | = a temporay VARIABLE for which M(res, i, j) = (*sub)(M(ptr, i, j) & var_temp_new(), *(sub)() ^=====================================================================*/ { VARIABLE *res; /* pointer to result structure */ double *a, *b; /* pointer to matrices */ int n, m; /* matrix dimensions */ int i; /* loop index */ /* Get space for result and ... */ n = NROW(ptr); m = NCOL(ptr); res = var_temp_new(TYPE(ptr) ,n , m); n *= m; a = MATR(ptr); b = MATR(res); /* ...to action. */ for(i = 0; i < n; i++) *b++ = (*sub)(*a++); return res; } VARIABLE *com_el(ptr) VARIABLE *ptr; /*====================================================================== ? Extracts specified elements from a matrix. Indexes are given by two | column vectors. The values of the elements of these vectors give | the required indexes. If there is only one index vector given | it is assumed to be column index and row index is set to scalar 0. | | If matrix x is, for example, | | 1 2 | 3 4 | | you get the first row by | | x[0, 0 1] | | or by | | x(0 1) | = A new temporary VARIABLE, whose size equals to | number of row indexes times number of column indexes. | & var_temp_new(), var_delete_temp() ^=====================================================================*/ { VARIABLE *res, /* result ... */ *par = NEXT(ptr); /* pointer to list of VARIABLES */ /* containig indexes */ static double defind = 0.0; double *ind1 = &defind, *ind2; int i, j, k, /* loop indexes */ rows, cols, /* no. of rows and columns in the matrix */ /* to be indexed. */ size1 = 1, size2, ind; rows = NROW(ptr); cols = NCOL(ptr); /* * check if scalar .... */ if (rows == 1 && cols == 1) { if (*MATR(par) != 0) error("Index out of bounds.\n"); if (NEXT(par) != NULL) if (*MATR(NEXT(par)) != 0) error("Index out of bounds.\n"); res = var_temp_new(TYPE(ptr),1,1); *MATR(res) = *MATR(ptr); return res; } /* The matrix will be indexed by two column vectors. If there is just one assume it's column index and make rowindex 0. */ if (NEXT(par) == NULL) { if (NROW(par) == rows && NCOL(par) == cols) { int logical = TRUE, onecount=0; double *dtmp; dtmp = MATR(par); for(i = 0; i < NROW(par)*NCOL(par); i++) if (dtmp[i] == 0) { } else if (dtmp[i] == 1) { onecount++; } else { logical = FALSE; break; } if (logical) { if (onecount == 0) return NULL; res = var_temp_new(TYPE(ptr),1,onecount); for(i=0,k=0; i < rows; i++) for(j=0; j < cols; j++) if (M(par,i,j) == 1) { memcpy(&M(res,0,k++),&M(ptr,i,j),sizeof(double)); } return res; } } ind2 = MATR(par); size2 = NCOL(par); cols *= rows; rows = 1; } else { ind1 = MATR(par); size1 = NCOL(par); size2 = NCOL(NEXT(par)); ind2 = MATR(NEXT(par)); } /* Space for result */ res = var_temp_new(TYPE(ptr), size1, size2); /* Extract the values (try making sense out of that if you feel like it). */ for(i = 0; i < size1; i++) { ind = (int)ind1[i]; for(j = 0; j < size2; j++) if (ind < rows && (int)ind2[j] < cols) memcpy(&M(res,i,j),&M(ptr,ind,(int)ind2[j]),sizeof(double)); else error("Index out of bounds.\n"); } return res; } VARIABLE *com_source(ptr) VARIABLE *ptr; /*====================================================================== ? Redirect input stream to a file, whose name is given. | @ FILE *math_in & ALLOCMEM, FREEMEM, fopen(), fclose(), error() ^=====================================================================*/ { char *name; /* Hold converted string (file name) */ FILE *save_in = math_in; /* Save previous input stream until */ /* we are done with the new file. */ /* convert the file name from ptr. */ name = var_to_string(ptr); /* Execute the file. */ if ((math_in = fopen(name,"r")) != NULL) { /* PrintOut("Executing commands from file, %s...\n", name); */ doread(); fclose(math_in); } else { PrintOut( "Source: Can't open file, %s.\n",name ); } math_in = save_in; FREEMEM(name); return (VARIABLE *)NULL; } VARIABLE *com_apply(ptr) VARIABLE *ptr; /*====================================================================== ? Executes given string. | & ALLOCMEM, FREEMEM, doit() ^=====================================================================*/ { VARIABLE *res; /* result pointer */ char *p, *q; /* holds the string to be executed, after */ /* conversion from structure VARIABLE * */ int i, j; /* just loop indexes */ /* Allocate space for the string... */ p = q = ALLOCMEM(NROW(ptr) * NCOL(ptr) + 1); /* ... convert it ... */ for(i = 0; i < NROW(ptr); i++) for(j = 0; j < NCOL(ptr); j++) *p++ = (char)M(ptr,i,j); *p = '\0'; /* ... and try executing it. */ res = doit( q ); FREEMEM(q); return res; } void mem_free(void *mem) /*====================================================================== ? Free memory given by argument, and unlink it from alloction list. | Currently FREEMEM(ptr) is defined to be mem_free(ptr). | & free() ~ mem_alloc(), mem_free_all() ^=====================================================================*/ { ALLOC_LIST *lst; #ifdef DEBUG tot--; fprintf(fplog,"free addr: %d total: %d\n", ALLOC_LST(mem), tot); fflush( fplog ); #endif /* if the list is empty return */ if ( (lst = (ALLOC_LIST *)ALLOC_HEAD) == (ALLOC_LIST *)NULL ) { #if 1 /* ????? */ free( ALLOC_LST(mem) ); #else fprintf( stderr, "SHOULD THIS HAPPEN ????\n" ); #endif return; } /* * it's not the header, look if it's in list at all */ if (ALLOC_PTR(lst) != mem) { for(; NEXT(lst); lst = NEXT(lst)) { if (ALLOC_PTR(NEXT(lst)) == mem) break; } /* * item was not found from the list. free ptr and return. */ if (NEXT(lst) == (ALLOC_LIST *)NULL) { free(ALLOC_LST(mem)); return; } /* * unlink */ NEXT(lst) = NEXT(NEXT(lst)); } /* * item was the header, unlink it */ else ALLOC_HEAD = NEXT(ALLOC_HEAD); /* * and at last return memory back to system */ free(ALLOC_LST(mem)); return; } void mem_free_all() /*====================================================================== ? Free all memory allocated since last entry of doread. | (actually free all memory from list ALLOCATIONS). | ~ mem_alloc(), mem_free(), doread(), error() ^=====================================================================*/ { ALLOC_LIST *lst, *lstn; for(lst = (ALLOC_LIST *)ALLOC_HEAD; lst;) { #ifdef DEBUG tot--; fprintf(fplog,"freeall addr: %d total: %d\n", lst, tot); fflush( fplog ); #endif lstn = NEXT(lst); free( (char *)lst ); lst = lstn; } ALLOC_HEAD = (LIST *)NULL; /* security */ return; } void *mem_alloc(size) size_t size; /*====================================================================== ? Allocate memory and link it to memory allocation list. | ~ calloc(), free(), error() ^=====================================================================*/ { ALLOC_LIST *lst; /* * try allocating memory */ if ((lst = (ALLOC_LIST *)calloc(size+sizeof(ALLOC_LIST), 1)) != NULL) { NEXT(lst) = (ALLOC_LIST *)ALLOC_HEAD; ALLOC_HEAD = (LIST *)lst; } else error("Can't alloc mem.\n"); #ifdef DEBUG tot++; fprintf(fplog,"alloc addr: %d size: %d total: %d\n", lst, size, tot); fflush( fplog ); #endif return ALLOC_PTR(lst); }