/* * This file implements a common mechanism for delivering messages * to the user. It also provides a way to selectively turn * on and off the status messages emanating from a part of the * program. * Call msg_on("foo"); to see all messages of the form: * msg("foo", ("printf-format", printf-args)); * Note how __FILE__ can be used in place of "foo"... * * This file does not use . This is because in the wonderful * world of distributed parallel, figuring out whose you're getting * and whether or not the names have been secretly scrambled is a complete * nightmare. The solution is to make the user pass in two function pointers * that are supposed to behave like vfprintf and fflush, and a void * which * acts like a FILE * (when passed to the two functions). How the caller * arranges these functions is not our problem. * The down-side is that picky compilers may complain about not having * prototypes for sscanf. Too bad... */ #include #include #include /* only for sscanf! */ #include "mpmy.h" #include "Malloc.h" #include "Msgs.h" #include "protos.h" #ifndef Static #define Static static #endif /* Don't increase MAXNAMES arbitrarily. If it grows much bigger */ /* than O(100), a different search algorithm would be appropriate. */ #define MAXNAMES 50 /* A substantially larger MAXFILES is probably a mistake. You'll run out */ /* of file descriptors soon enough anyway. */ #define MAXFILES 12 /* The sum of the lengths of all the NAMES (including terminal null). */ #define NAMEPOOLLENGTH (MAXNAMES*32) Static int nnames; Static struct nt_s{ const char *name; int status; struct nt_s *next; } name_tbl[MAXNAMES], *first; Static int nfiles; Static struct ft_s{ void *fp; int (*vfprintf_like)(void *, const char *, va_list); int (*fflush_like)(void *); } file_tbl[MAXFILES]; char name_pool[NAMEPOOLLENGTH] ; char *poolptr = name_pool; char *poolend = name_pool + NAMEPOOLLENGTH; Static int look_name(const char *name); Static int Msg_restriction(const char *arg); int _Msg_enabled = 1; Static int _Msg_flushalways = 0; Static int called_addfile = 0; int Msg_addfile(void *fp, int (*vfprintf_like)(void *, const char *, va_list), int (*fflush_like)(void *)){ if( nfiles == MAXFILES ) return -1; file_tbl[nfiles].fp = fp; file_tbl[nfiles].vfprintf_like = vfprintf_like; file_tbl[nfiles].fflush_like = fflush_like; nfiles++; called_addfile = 1; return 0; } int Msg_delfile(void *fp){ int i; for(i=0; ivfprintf_like)(ft->fp, fmt, args); va_end(args); if( _Msg_flushalways && ft->fflush_like) (*ft->fflush_like)(ft->fp); } #ifndef NO_STDIO if( nfiles == 0 && !called_addfile ){ va_start(args, fmt); vfprintf(stderr, fmt, args); if( _Msg_flushalways ) fflush(stderr); va_end(args); extra = 1; } #endif } --recursion; _Msg_enabled = save; return nfiles+extra; } int Msg_doalist(const char *fmt, va_list alist) { int i; struct ft_s *ft; int save; int extra = 0; save = _Msg_enabled; if( recursion++ == 0 ){ for(i=0; ivfprintf_like)(ft->fp, fmt, alist); if( _Msg_flushalways && ft->fflush_like ) (*ft->fflush_like)(ft->fp); } #ifndef NO_STDIO if( nfiles == 0 && !called_addfile ){ vfprintf(stderr, fmt, alist); if( _Msg_flushalways ) fflush(stderr); extra = 1; } #endif } --recursion; _Msg_enabled = save; return nfiles+extra; } void Msg_turnon(const char *msg_turn_on){ char *copy; char *msg_key; /* You can turn off msgs with a "nomsgs" or a null string or an */ /* empty string. */ if( msg_turn_on == NULL || msg_turn_on[0] == '\0' || strcmp(msg_turn_on, "nomsgs")==0 ){ Msg_set_enable(0); return; } copy = strcpy(Malloc(strlen(msg_turn_on)+1), msg_turn_on); /* Look for comma or space separated arguments to Msg_on */ for(msg_key = strtok(copy, " ,\t\n"); msg_key; msg_key = strtok(NULL, " ,\t\n")){ char *restriction_begin = strchr(msg_key, ':'); if( restriction_begin ){ if( !Msg_restriction(restriction_begin+1) ) continue; *restriction_begin='\0'; } Msg_on(msg_key); Msg_do("Turning on Msgs for \"%s\"\n", msg_key); /* Do we need to add "./" to the key in addition??? */ if( strncmp( __FILE__, "./", 2) == 0 && strncmp( msg_key, "./", 2 ) != 0 ){ char *dot_slash_key = Malloc(strlen(msg_key)+3); /* sprintf might be easier, but we are trying to avoid */ /* using stdio. */ /* sprintf(dot_slash_key, "./%s", msg_key) */ strcpy(dot_slash_key, "./"); strcat(dot_slash_key, msg_key); Msg_on(dot_slash_key); Msg_do("Turning on Msgs for \"%s\"\n", dot_slash_key); Free(dot_slash_key); } } Free(copy); } int Msg_flush(void) { int i, ret; struct ft_s *ft; int save; save = _Msg_enabled; ret = 0; if( recursion++ == 0 ){ for(i=0; ifflush_like ) ret |= (*ft->fflush_like)(ft->fp); } } --recursion; _Msg_enabled = save; return ret; } Static int look_name(const char *name) { int i; struct nt_s *last, *this; unsigned int len; last = NULL; this = first; if( name == NULL ){ Warning("look_name(NULL)\n"); return -1; } while( this && this->name && strcmp(name, this->name) != 0 ) { last = this; this = this->next; } if( this ){ if( last ){ last->next = this->next; this->next = first; first = this; } return this - name_tbl; }else{ /* Create a new entry. Add it to the front. */ /* NOTE: we never free any of this! */ if(nnames == MAXNAMES){ return -1; } len = strlen(name)+1; if( poolptr + len >= poolend ){ return -1; } i = nnames++; strcpy(poolptr, name); name_tbl[i].name = poolptr; poolptr += len; name_tbl[i].next = first; first = &name_tbl[i]; return i; } } Static int Msg_restriction(const char *arg){ int first, last; if( strchr(arg, '-') ){ if( sscanf(arg, "%d-%d", &first, &last) != 2 ){ Msg_do("Unparseable msg restriction: \"%s\"\n", arg); return 0; } return MPMY_Procnum() <= last && MPMY_Procnum()>= first ; }else{ if( sscanf(arg, "%d", &first) != 1 ){ Msg_do("Unparseable msg restriction: \"%s\"\n", arg); return 0; } return MPMY_Procnum() == first; } } #ifdef STANDALONE #include /* Of course, Sun's stdio.h doesn't declare fflush or vfprintf! */ int vfprintf(FILE *, const char *, va_list); int fflush(FILE *); main(int argc, char **argv){ FILE *aux; Msg_addfile(stdout, vfprintf, fflush); aux = fopen("Msg.out", "w"); if( aux == NULL ){ fprintf(stderr, "Couldn't fopen("Msg.out"), errno=%d\n", errno); exit(1); } Msg_addfile(aux, vfprintf, fflush); Msg_on("foo"); Msg("foo", ("Hello world foo seventeen=%d\n", 17)); Msg("bar", ("Hello bar")); Msg_on("Msgs.c"); Msg_on("bar"); Msg("bar", ("Hello bar pi=%g\n", 3.14159)); Msglno("bar", ("What was that value again??... %g\n,", 3.1415)); Msgf(("This is a Msgf message.\n")); Msg_off("foo"); Msg_off(__FILE__); Msgf(("This is a blocked Msgf message.\n")); Msg("foo", ("Not seen.\n")); exit(0); } #endif