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