Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

vmdconsole.c

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr
00003  *cr            (C) Copyright 1995-2019 The Board of Trustees of the
00004  *cr                        University of Illinois
00005  *cr                         All Rights Reserved
00006  *cr
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  * RCS INFORMATION:
00011  *
00012  *      $RCSfile: vmdconsole.c,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.13 $       $Date: 2019/01/17 21:21:03 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *
00019  * vmd console redirector
00020  * (c) 2006-2009 Axel Kohlmeyer <akohlmey@cmm.chem.upenn.edu>
00021  * 
00022  ***************************************************************************/
00023 
00024 #if defined(VMDTKCON)
00025 
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <stdarg.h>
00029 #include <string.h>
00030 #include <errno.h>
00031 
00032 #include "vmdconsole.h"
00033 #include "WKFThreads.h"
00034 
00035 #ifdef __cplusplus
00036 extern "C" {
00037 #endif
00038 
00039 /* structure for linked list of pending messages. */
00040 struct vmdcon_msg 
00041 {
00042     char *txt;
00043     int  lvl;
00044     struct vmdcon_msg *next;
00045 };
00046 
00047 static struct vmdcon_msg *vmdcon_pending=NULL;
00048 static struct vmdcon_msg *vmdcon_lastmsg=NULL;
00049 
00050 static struct vmdcon_msg *vmdcon_logmsgs=NULL;
00051 static struct vmdcon_msg *vmdcon_lastlog=NULL;
00052 static int vmdcon_max_loglen=2000;
00053 static int vmdcon_loglen=0;
00054     
00055 /* static buffer size for vmdcon_printf */
00056 static const int vmdcon_bufsz=4092;
00057 
00058 /* path to console text widget */
00059 static char *vmdcon_wpath=NULL;
00060 
00061 /* current insertion mark */
00062 static char *vmdcon_mark=NULL;
00063 
00064 /* opaque pointer to the current tcl interpreter */
00065 static void *vmdcon_interp=NULL;
00066 
00067 /* output destination status. */
00068 static int vmdcon_status=VMDCON_UNDEF;
00069 
00070 /* output loglevel. default to print all.*/
00071 static int vmdcon_loglvl=VMDCON_ALL;
00072 
00073 /* mutex to lock access to status variables */
00074 static wkf_mutex_t vmdcon_status_lock;
00075 static wkf_mutex_t vmdcon_output_lock;
00076 
00077 /* initialize vmdcon */
00078 void vmdcon_init(void) 
00079 {
00080     wkf_mutex_init(&vmdcon_status_lock);
00081     wkf_mutex_init(&vmdcon_output_lock);
00082     vmdcon_set_status(VMDCON_NONE, NULL);
00083 }
00084 
00085 /* report current vmdcon status */
00086 int vmdcon_get_status(void)
00087 {
00088     return vmdcon_status;
00089 }
00090  
00091 /* set current vmdcon status */
00092 void vmdcon_set_status(int status, void *interp)
00093 {
00094     wkf_mutex_lock(&vmdcon_status_lock);
00095     if (interp != NULL) vmdcon_interp=interp;
00096     vmdcon_status=status;
00097     tcl_vmdcon_set_status_var(vmdcon_interp, status);
00098     wkf_mutex_unlock(&vmdcon_status_lock);
00099 }
00100 
00101 /* set current vmdcon log level */
00102 void vmdcon_set_loglvl(int lvl)
00103 {
00104     wkf_mutex_lock(&vmdcon_status_lock);
00105     vmdcon_loglvl=lvl;
00106     wkf_mutex_unlock(&vmdcon_status_lock);
00107 }
00108 
00109 /* set current vmdcon log level */
00110 int vmdcon_get_loglvl(void)
00111 {
00112     return vmdcon_loglvl;
00113 }
00114 
00115 /* turn on text mode processing */
00116 void vmdcon_use_text(void *interp) 
00117 {
00118     vmdcon_set_status(VMDCON_TEXT, interp);
00119 }
00120 
00121 /* turn on tk text widget mode processing */
00122 void vmdcon_use_widget(void *interp) 
00123 {
00124     vmdcon_set_status(VMDCON_WIDGET, interp);
00125 }
00126 
00127 /* register a tk/tcl widget to be the console window.
00128  * we get the widget path directly from tcl, 
00129  * so we have to create a copy (and free it later).
00130  * we also need a pointer to the tcl interpreter.
00131  */
00132 int vmdcon_register(const char *w_path, const char *mark, void *interp)
00133 {
00134     wkf_mutex_lock(&vmdcon_status_lock);
00135     vmdcon_interp=interp;
00136 
00137     /* unregister current console widget */
00138     if(w_path == NULL) {
00139         if (vmdcon_wpath != NULL) {
00140             free(vmdcon_wpath);
00141             free(vmdcon_mark);
00142         }
00143         vmdcon_wpath=NULL;
00144         vmdcon_mark=NULL;
00145         /* we have to indicate that no console is available */
00146         if (vmdcon_status == VMDCON_WIDGET) vmdcon_status=VMDCON_NONE;
00147     } else {
00148         int len;
00149         
00150         if (vmdcon_wpath != NULL) {
00151             free(vmdcon_wpath);
00152             free(vmdcon_mark);
00153         }
00154     
00155         len=strlen(w_path);
00156         vmdcon_wpath=(char*)malloc(len+1);
00157         strcpy(vmdcon_wpath, w_path);
00158         len=strlen(mark);
00159         vmdcon_mark=(char*)malloc(len+1);
00160         strcpy(vmdcon_mark, mark);
00161     }
00162     wkf_mutex_unlock(&vmdcon_status_lock);
00163 
00164     /* try to flush pending console log text. */
00165     return vmdcon_purge();
00166 }
00167 
00168 /* append text from to console log buffer to queue. */
00169 int vmdcon_showlog(void)
00170 {
00171     struct vmdcon_msg *log, *msg;
00172 
00173     wkf_mutex_lock(&vmdcon_output_lock);
00174     log=vmdcon_logmsgs;
00175     do {
00176         /* append to message queue. */
00177         msg=(struct vmdcon_msg *)malloc(sizeof(struct vmdcon_msg));
00178         msg->txt=(char *) malloc(strlen(log->txt)+1);
00179         msg->lvl=VMDCON_ALWAYS;
00180         strcpy(msg->txt,log->txt);
00181         msg->next=NULL;
00182     
00183         if (vmdcon_pending == NULL) {
00184             vmdcon_pending=msg;
00185             vmdcon_lastmsg=msg;
00186         } else {
00187             vmdcon_lastmsg->next=msg;
00188             vmdcon_lastmsg=msg;
00189         }
00190         log=log->next;
00191     } while (log->next != NULL);
00192 
00193     /* terminate the dmesg output with a newline */
00194     msg=(struct vmdcon_msg *)malloc(sizeof(struct vmdcon_msg));
00195     msg->txt=(char *) malloc(strlen("\n")+1);
00196     msg->lvl=VMDCON_ALWAYS;
00197     strcpy(msg->txt,"\n");
00198     msg->next=NULL;
00199     
00200     if (vmdcon_pending == NULL) {
00201         vmdcon_pending=msg;
00202         vmdcon_lastmsg=msg;
00203     } else {
00204         vmdcon_lastmsg->next=msg;
00205         vmdcon_lastmsg=msg;
00206     }
00207     log=log->next;
00208 
00209     wkf_mutex_unlock(&vmdcon_output_lock);
00210     return vmdcon_purge();
00211 }
00212 
00213 /* append text to console log queue.
00214  * we have to make copies as we might get handed 
00215  * a tcl object or a pointer to some larger buffer. */
00216 int vmdcon_append(int level, const char *txt, int len) 
00217 {
00218     struct vmdcon_msg *msg;
00219     char *buf;
00220 
00221     /* len=0: don't print. len=-1, autodetect. */
00222     if (len == 0 ) return 0;
00223     if (len < 0) {
00224         len=strlen(txt);
00225     }
00226 
00227     wkf_mutex_lock(&vmdcon_output_lock);
00228     
00229     /* append to message queue. */
00230     /* but don't print stuff below the current loglevel */
00231     if (level >= vmdcon_loglvl) {
00232       /* create copy of text. gets free'd after it has been 'printed'. */
00233       buf=(char *)calloc(len+1,1);
00234       strncpy(buf,txt,len);
00235     
00236       msg=(struct vmdcon_msg *)malloc(sizeof(struct vmdcon_msg));
00237       msg->txt=buf;
00238       msg->lvl=level;
00239       msg->next=NULL;
00240       
00241       if (vmdcon_pending == NULL) {
00242         vmdcon_pending=msg;
00243         vmdcon_lastmsg=msg;
00244       } else {
00245         vmdcon_lastmsg->next=msg;
00246         vmdcon_lastmsg=msg;
00247       }
00248     }
00249     
00250     /* messages are added to the log regardless of loglevel.
00251      * this way we can silence the log window and still retrieve
00252      * useful information with 'vmdcon -dmesg'. */
00253     buf=(char *)calloc(len+1,1);
00254     strncpy(buf,txt,len);
00255 
00256     /* append to log message list. */
00257     msg=(struct vmdcon_msg *)malloc(sizeof(struct vmdcon_msg));
00258     msg->txt=buf;
00259     msg->lvl=level;
00260     msg->next=NULL;
00261         
00262     if (vmdcon_logmsgs == NULL) {
00263         vmdcon_logmsgs=msg;
00264         vmdcon_lastlog=msg;
00265         ++vmdcon_loglen;
00266     } else {
00267         vmdcon_lastlog->next=msg;
00268         vmdcon_lastlog=msg;
00269         ++vmdcon_loglen;
00270     }
00271     
00272     /* remove message from the front of the queue
00273      * in case we have too long a list */
00274     while (vmdcon_loglen > vmdcon_max_loglen) {
00275         msg=vmdcon_logmsgs;
00276         vmdcon_logmsgs=msg->next;
00277         free(msg->txt);
00278         free(msg);
00279         --vmdcon_loglen;
00280     }
00281     
00282     wkf_mutex_unlock(&vmdcon_output_lock);
00283 
00284     return 0;
00285 }
00286 
00287 /* flush current message queue to a registered 
00288  * console widget, if such a thing exists.
00289  * since vmdcon_append() allocates the storage,
00290  * for everything, we have to free the msg structs
00291  * and the strings. */
00292 int vmdcon_purge(void) 
00293 {
00294     struct vmdcon_msg *msg;
00295     const char *res;
00296 
00297     wkf_mutex_lock(&vmdcon_status_lock);
00298     /* purge message queue only if we have a working console window */
00299     if ( ! ((vmdcon_status == VMDCON_UNDEF) || (vmdcon_status == VMDCON_NONE)
00300         || ((vmdcon_status == VMDCON_WIDGET) &&
00301             ((vmdcon_interp == NULL) || (vmdcon_wpath == NULL))) ) ) {
00302 
00303         wkf_mutex_lock(&vmdcon_output_lock);
00304         while (vmdcon_pending != NULL) {
00305             msg=vmdcon_pending;
00306 
00307             switch (vmdcon_status) {
00308               case VMDCON_TEXT:
00309                   fputs(msg->txt,stdout);
00310                   break;
00311                   
00312               case VMDCON_WIDGET: 
00313                   res = tcl_vmdcon_insert(vmdcon_interp, vmdcon_wpath, 
00314                                           vmdcon_mark, msg->txt);
00315                   /* handle errors writing to a tcl console window.
00316                    * unregister widget, don't free current message 
00317                    * and append error message into holding buffer. */
00318                   if (res) {
00319                       wkf_mutex_unlock(&vmdcon_status_lock);
00320                       vmdcon_register(NULL, NULL, vmdcon_interp);
00321                       wkf_mutex_unlock(&vmdcon_output_lock);
00322                       vmdcon_printf(VMDCON_ERROR,
00323                                     "Problem writing to text widget: %s\n", res);
00324                       return 1;
00325                   }
00326                   break;
00327 
00328               default:
00329                   /* unknown console type */
00330                   return 1;
00331             }
00332             free(msg->txt);
00333             vmdcon_pending=msg->next;
00334             free(msg);
00335 
00336         }
00337         if (vmdcon_status == VMDCON_TEXT) 
00338             fflush(stdout);
00339 
00340         wkf_mutex_unlock(&vmdcon_output_lock);
00341     }
00342     wkf_mutex_unlock(&vmdcon_status_lock);
00343     return 0;
00344 }
00345 
00346 /* emulate printf. unfortunately, we cannot rely on 
00347  * snprintf being available, so we have to write to
00348  * a very large buffer and then free it. :-( */
00349 int vmdcon_printf(const int lvl, const char *fmt, ...) 
00350 {
00351     va_list ap;
00352     char *buf;
00353     int len;
00354 
00355     /* expand formated output into a single string */
00356     buf = (char *)malloc(vmdcon_bufsz);
00357     va_start(ap, fmt);
00358     len = vsprintf(buf, fmt, ap);
00359 
00360     /* check result. we may get a segfault, but if not
00361      * let the user know that he/she is in trouble. */
00362     if (len >= vmdcon_bufsz) {
00363         fprintf(stderr,"WARNING! buffer overflow in vmdcon_printf. %d vs %d.\n",
00364                 len, vmdcon_bufsz);
00365         free(buf);
00366         errno=ERANGE;
00367         return -1;
00368     }
00369 
00370     /* prefix message with info level... or not. */
00371     switch (lvl) {
00372       case VMDCON_INFO:
00373         vmdcon_append(lvl, "Info) ", 6);
00374         break;
00375 
00376       case VMDCON_WARN:
00377         vmdcon_append(lvl, "Warning) ", 9);
00378         break;
00379 
00380       case VMDCON_ERROR:
00381         vmdcon_append(lvl, "ERROR) ", 7);
00382         break;
00383 
00384       default:  
00385         break;
00386     }
00387 
00388     vmdcon_append(lvl, buf, len);
00389     vmdcon_purge();
00390 
00391     free(buf);
00392     return 0;    
00393 }
00394 
00395 /* emulate fputs for console. */
00396 int vmdcon_fputs(const int lvl, const char *string) 
00397 {
00398     /* prefix message with info level... or not. */
00399     switch (lvl) {
00400       case VMDCON_INFO:
00401         vmdcon_append(lvl, "Info) ", 6);
00402         break;
00403 
00404       case VMDCON_WARN:
00405         vmdcon_append(lvl, "Warning) ", 9);
00406         break;
00407 
00408       case VMDCON_ERROR:
00409         vmdcon_append(lvl, "ERROR) ", 7);
00410         break;
00411 
00412       default:  
00413         break;
00414     }
00415 
00416     vmdcon_append(lvl, string, -1);
00417     vmdcon_purge();
00418 
00419     return 0;    
00420 }
00421 
00422 #ifdef __cplusplus
00423 }
00424 #endif
00425 
00426 #endif

Generated on Thu Mar 28 02:44:22 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002