1 /* 2 * dsp_xm.c - Display results in a Motif window. 3 * 4 * Copyright (C) 1995, 1996 by Scott C. Gray 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, write to the Free Software 18 * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 19 * 20 * You may contact the author : 21 * e-mail: gray@voicenet.com 22 * grays@xtend-tech.com 23 * gray@xenotropic.com 24 * 25 * Note: The Motif code was supplied by John Griffin 26 * <belved!JOHNG@uunet.uu.net> and has been modified 27 * slightly by Scott C. Gray. 28 */ 29 #include <stdio.h> 30 #include <ctype.h> 31 #include "sqsh_config.h" 32 #include "sqsh_debug.h" 33 #include "sqsh_error.h" 34 #include "sqsh_expand.h" 35 #include "sqsh_global.h" 36 #include "sqsh_init.h" 37 #include "dsp.h" 38 39 /*-- Current Version --*/ 40 #if !defined(lint) && !defined(__LINT__) 41 static char RCS_Id[] = "$Id: dsp_x.c,v 1.8 2013/05/07 21:18:02 mwesdorp Exp $"; 42 USE(RCS_Id) 43 #endif /* !defined(lint) */ 44 45 #if !defined(USE_X11) 46 47 int dsp_x( output, cmd, flags, dsp_func ) 48 dsp_out_t *output; 49 CS_COMMAND *cmd; 50 int flags; 51 dsp_t *dsp_func; 52 { 53 fprintf( stderr, "dsp_x: X11 support not compiled into sqsh\n" ); 54 return DSP_FAIL; 55 } 56 57 #else 58 59 #include <X11/Intrinsic.h> 60 #include <X11/StringDefs.h> 61 62 /* 63 * cb_data_t: 64 * 65 * This mostly useless data structure is used to pass around important 66 * information between callbacks. Mainly, this is information about 67 * objects whose state can change according to callbacks. 68 */ 69 typedef struct cb_data_st { 70 int c_fd; /* File descriptor for communications */ 71 int c_lines; /* Number of lines displayed */ 72 int c_lastpos; 73 Widget c_text; /* Text widget */ 74 Widget c_cmd; 75 Widget c_lbl; 76 XtInputId c_id; /* Input id */ 77 } cb_data_t; 78 79 /*-- Prototypes --*/ 80 static int dsp_x_init _ANSI_ARGS(( int, int, int )); 81 82 /* 83 * dsp_x: 84 * 85 * Display routine to display the current result into an x window. This 86 * function is interesting in that it creates a pipe-line between itself 87 * and the dsp_horiz display style. 88 */ 89 int dsp_x( output, cmd, flags, dsp_func ) 90 dsp_out_t *output; 91 CS_COMMAND *cmd; 92 int flags; 93 dsp_t *dsp_func; 94 { 95 int fd_pair[2]; /* Pair of pipes to communicate */ 96 int ret; /* Return value from display method */ 97 int width; /* Width of screen */ 98 int height; /* Height of screen */ 99 char number[20]; /* Holder for number */ 100 char *cp; 101 int orig_fd; 102 int fd; 103 int orig_width; 104 int i; 105 106 /* 107 * First, we would like to figure out how large our X window 108 * really is. 109 */ 110 orig_width = g_dsp_props.p_width; 111 if (*g_dsp_props.p_xgeom == '\0') 112 { 113 width = 80; 114 height = 25; 115 } 116 else 117 { 118 i = 0; 119 for (cp = g_dsp_props.p_xgeom; *cp != '\0' && isdigit( (int) *cp); ++cp) 120 { 121 number[i++] = *cp; 122 } 123 number[i] = '\0'; 124 125 width = atoi(number); 126 127 if (*cp != '\0') 128 { 129 height = atoi( cp + 1 ); 130 } 131 else 132 { 133 height = 25; 134 } 135 136 if (width < 30) 137 { 138 width = 80; 139 } 140 141 if (height < 10) 142 { 143 height = 25; 144 } 145 } 146 147 DBG(sqsh_debug(DEBUG_DISPLAY,"dsp_x: width = %d, height = %d\n", 148 width, height);) 149 150 /*-- Create a pair of pipes to talk through --*/ 151 if (pipe(fd_pair) == -1) 152 { 153 fprintf( stderr, "dsp_x: Unable to create communications pipe\n" ); 154 return DSP_FAIL; 155 } 156 157 switch (fork()) 158 { 159 case -1: /* Error condition */ 160 fprintf( stderr, "dsp_x: Unable to create child process: %s\n", 161 strerror(errno) ); 162 close( fd_pair[0] ); 163 close( fd_pair[1] ); 164 return DSP_FAIL; 165 166 case 0: /* Child process */ 167 /* 168 * Keep a handle on the file descriptor to be used to communicate 169 * with our parent (sqsh). And close the other file descriptor 170 * that we no longer need. 171 */ 172 fd = fd_pair[0]; 173 close( fd_pair[1] ); 174 break; 175 176 default: /* Parent process */ 177 178 /* 179 * Close child's socket. 180 */ 181 close( fd_pair[0] ); 182 183 /* 184 * Since we will be redirecting the file descriptor for 185 * output, we want to make sure it has flushed its contents 186 * before we begin. 187 */ 188 dsp_fflush( output ); 189 190 /* 191 * Because we are going to replace the stdout for this process 192 * we want to make sure we can restore it before we return. 193 */ 194 orig_fd = dup( output->o_fd ); 195 196 /* 197 * Now attach the remaining file descriptor to our output for 198 * the life of the selected display method. 199 */ 200 dup2( fd_pair[1], output->o_fd ); 201 close( fd_pair[1] ); 202 203 /* 204 * Allow the display method to do whatever it does, however 205 * while it runs, the output will be piped into our child 206 * process that should be displaying it into an X window. 207 */ 208 g_dsp_props.p_width=2040; 209 ret = dsp_func( output, cmd, flags ); 210 g_dsp_props.p_width=orig_width; 211 212 /* 213 * Now restore the file descriptors back to their original 214 * state. After that, since output has most likely reached 215 * EOF, we need to clear this error condition before returning, 216 * otherwise OS's, like SunOS, will not be able to use it. 217 */ 218 dup2( orig_fd, output->o_fd ); 219 close( orig_fd ); 220 221 return ret; 222 } 223 224 dsp_x_init( fd, width, height ); 225 exit(0); 226 } 227 228 /******************************************************************** 229 ** 230 ** X11/MOTIF 231 ** 232 ********************************************************************/ 233 234 #if defined(USE_MOTIF) 235 236 /*-- Include the X headers --*/ 237 #include <Xm/Command.h> 238 #include <Xm/Label.h> 239 #include <Xm/Form.h> 240 #include <Xm/ScrolledW.h> 241 #include <Xm/Text.h> 242 #include <Xm/RowColumn.h> 243 #include <Xm/PushB.h> 244 #include <Xm/PanedW.h> 245 #include <Xm/MainW.h> 246 247 /*-- Prototypes --*/ 248 static void dsp_x_input_cb( XtPointer, int*, XtInputId* ); 249 static void dsp_x_ok_cb ( Widget, XtPointer, XtPointer ); 250 251 static int dsp_x_init( fd, width, height ) 252 int fd; 253 int width; 254 int height; 255 { 256 int nlines; 257 258 /*-- Widgets that make up our window --*/ 259 Widget w_top; /* Top level widget */ 260 Widget w_main; /* Top level widget */ 261 Widget w_res_form; /* Form constraing */ 262 Widget w_res; /* Text Widget */ 263 Widget w_sql_form; 264 Widget w_sql_text; 265 Widget w_paned; 266 Widget w_btn_form; 267 Widget w_btn_ok; 268 XmString s_ok; 269 cb_data_t cd; /* Data for callbacks */ 270 XtInputId id; /* Id of input source */ 271 Dimension dim; 272 int argc; 273 char *argv[1]; 274 char *cp; 275 char *xwin_title = NULL; 276 varbuf_t *exp_buf = NULL; 277 278 /* 279 * At this point we are in the child process, the rest is pretty 280 * easy. We simply open a window and simply place every line we 281 * receive from the parent through the fd file descriptor 282 * into a window. 283 * sqsh-2.1.7 - Pass on a window title through argv[0]. 284 */ 285 env_get( g_env, "xwin_title", &xwin_title ); 286 if (xwin_title != NULL && *xwin_title != '\0') 287 { 288 exp_buf = varbuf_create( 64 ); 289 290 if (exp_buf == NULL) 291 { 292 fprintf( stderr, "sqsh: %s\n", sqsh_get_errstr() ); 293 sqsh_exit( 255 ); 294 } 295 296 if (sqsh_expand( xwin_title, exp_buf, 0 ) == False) 297 { 298 fprintf( stderr, "sqsh: Error expanding $xwin_title: %s\n", 299 sqsh_get_errstr() ); 300 sqsh_exit( 255 ); 301 } 302 argc = 1; 303 argv[0] = varbuf_getstr( exp_buf ); 304 } 305 else 306 { 307 argc = 0; 308 argv[0] = NULL; 309 } 310 311 /* 312 * Calculate the number of lines in the SQL Text buffer. 313 * sqsh-2.1.7 : Set a maximum of 10 lines for the SQL buffer, 314 * otherwise it might eat up all the X window space in case of a 315 * large SQL batch. 316 */ 317 nlines = 0; 318 for (cp = varbuf_getstr(g_sqlbuf); *cp != '\0' && nlines < 10; ++cp) 319 { 320 if (*cp == '\n') 321 { 322 ++nlines; 323 } 324 } 325 326 w_top = XtInitialize( "sqsh", "Sqsh", NULL, 0, &argc, argv ); 327 328 w_main = 329 XtVaCreateManagedWidget("main", xmMainWindowWidgetClass, w_top, 330 NULL); 331 332 w_paned = 333 XtVaCreateManagedWidget("pane", xmPanedWindowWidgetClass, w_main, 334 XmNallowResize, TRUE, 335 NULL); 336 337 w_sql_form = 338 XtVaCreateManagedWidget("sql_form", xmScrolledWindowWidgetClass, w_paned, 339 XmNallowResize, TRUE, 340 NULL); 341 342 w_sql_text = 343 XtVaCreateManagedWidget( "sql_text", xmTextWidgetClass, w_sql_form, 344 XmNscrollingPolicy, XmAUTOMATIC, 345 XmNeditMode, XmMULTI_LINE_EDIT, 346 XmNeditable, 0, 347 XmNcolumns, width, 348 XmNrows, nlines, 349 XmNresizeWidth, 1, 350 NULL); 351 352 /* 353 * Populate the sql text box with the sql command being 354 * processed. 355 */ 356 XmTextSetString(w_sql_text, varbuf_getstr(g_sqlbuf) ); 357 358 w_res_form = 359 XtVaCreateManagedWidget("w_res_form", xmScrolledWindowWidgetClass, 360 w_paned, 361 NULL); 362 363 w_res = 364 XtVaCreateManagedWidget( "text", xmTextWidgetClass, w_res_form, 365 XmNscrollingPolicy, XmAUTOMATIC, 366 XmNeditMode, XmMULTI_LINE_EDIT, 367 XmNeditable, 0, 368 XmNrows, height, 369 XmNresizeWidth, 1, 370 XmNcolumns, width, 371 NULL); 372 373 w_btn_form = 374 XtVaCreateManagedWidget("btn_form", xmFormWidgetClass, w_paned, 375 XmNrows, 1, 376 XmNcolumns, 1, 377 XmNresize, 0, 378 NULL); 379 380 s_ok = XmStringCreateLocalized( "Done" ); 381 382 w_btn_ok = 383 XtVaCreateManagedWidget( "ok_button", xmPushButtonWidgetClass, w_btn_form, 384 XmNlabelString, s_ok, 385 XmNleftAttachment, XmATTACH_FORM, 386 XmNrightAttachment, XmATTACH_FORM, 387 XmNbottomAttachment, XmATTACH_FORM, 388 XmNtopAttachment, XmATTACH_FORM, 389 NULL ); 390 391 XtAddCallback( w_btn_ok, XmNactivateCallback, dsp_x_ok_cb, &cd ); 392 393 id = XtAddInput( fd, (XtPointer)XtInputReadMask, 394 (XtInputCallbackProc)dsp_x_input_cb, (XtPointer)&cd ); 395 396 /* 397 * We have already attached a cd_data_t to all of our callbacks, so 398 * now we need to populate it with enough info to get work done. 399 */ 400 cd.c_fd = fd; 401 cd.c_lines = 0; 402 cd.c_lastpos = 0; 403 cd.c_text = w_res; 404 cd.c_id = id; 405 406 XtRealizeWidget( w_top ); 407 /* 408 * Ok, We want to prevent the button from being resized. The easiest 409 * way to accomplish that is to set the max and the min. 410 */ 411 XtVaGetValues( w_btn_form, 412 XmNheight, &dim, 413 NULL); 414 XtVaSetValues( w_btn_form, 415 XmNpaneMaximum, dim, 416 XmNpaneMinimum, dim, 417 NULL); 418 XtMainLoop(); 419 420 if (exp_buf != NULL) 421 varbuf_destroy( exp_buf ); 422 exit(0); 423 } 424 425 /* 426 * dsp_x_input_cb(): 427 * 428 * This function is automatically called whenever input is received 429 * from our parent process via a file descriptor. It is responsible 430 * for appending any text received onto the end of the text widget. 431 */ 432 static void dsp_x_input_cb (client_data, fd, id) 433 XtPointer client_data; 434 int *fd; 435 XtInputId *id; 436 { 437 char buffer[2048]; /* Read up to 2K of input */ 438 int n; 439 cb_data_t *cd; 440 441 cd = (cb_data_t*)client_data; 442 443 /* 444 * Read input from the file descriptor. 445 */ 446 n = read( *fd, buffer, sizeof(buffer) - 1 ); 447 448 /* 449 * If we hit an error or EOF, then close the file descriptor 450 * and remove it from being an X input source. 451 */ 452 if (n <= 0) 453 { 454 close( *fd ); 455 cd->c_fd = -1; 456 XtVaSetValues(cd->c_text, XmNcursorPosition, 0, 457 XmNtopCharacter, 0, 458 NULL); 459 XtRemoveInput( cd->c_id ); 460 } 461 else 462 { 463 buffer[n] = '\0'; 464 465 /* 466 * Append the new buffer to what we already have displayed 467 * in the text window. 468 */ 469 XmTextReplace( cd->c_text, cd->c_lastpos, cd->c_lastpos + n, buffer ); 470 471 cd->c_lastpos += n; 472 } 473 } 474 475 static void dsp_x_ok_cb (w, client_data, call_data) 476 Widget w; 477 XtPointer client_data; 478 XtPointer call_data; 479 { 480 exit(0); 481 } 482 483 #else /* USE_MOTIF */ 484 485 /******************************************************************** 486 ** 487 ** X11/ATHENA 488 ** 489 ********************************************************************/ 490 491 /*-- Include the X headers --*/ 492 #include <X11/Xaw/Command.h> 493 #include <X11/Xaw/Label.h> 494 #include <X11/Xaw/Form.h> 495 #include <X11/Xaw/Viewport.h> 496 #include <X11/Xaw/AsciiText.h> 497 #include <X11/Xaw/Text.h> 498 #include <X11/Xaw/TextSrc.h> 499 #include <X11/Xaw/TextSink.h> 500 #include <X11/Xaw/AsciiSrc.h> 501 #include <X11/Xaw/AsciiSink.h> 502 503 /*-- Prototypes --*/ 504 static void dsp_x_input_cb( XtPointer, int*, XtInputId* ); 505 static void dsp_x_ok_cb ( Widget, XtPointer, XtPointer ); 506 507 static int dsp_x_init( fd, width, height ) 508 int fd; 509 int width; 510 int height; 511 { 512 Widget w_top; /* Top level widget */ 513 Widget w_form; /* Form constraing */ 514 Widget w_text; /* Text Widget */ 515 Widget w_cmd; /* Command widget */ 516 Widget w_lbl; /* Label widget */ 517 cb_data_t cd; /* Data for callbacks */ 518 XtInputId id; /* Id of input source */ 519 int text_width; 520 int argc; 521 char *argv[1]; 522 char *xwin_title = NULL; 523 varbuf_t *exp_buf = NULL; 524 525 XFontStruct *font = NULL; /* Font for text widget */ 526 527 /* 528 * At this point we are in the child process, the rest is pretty 529 * easy. We simply open a window and simply place every line we 530 * receive from the parent through the fd file descriptor 531 * into a window. 532 * sqsh-2.1.7 - Pass on a window title through argv[0]. 533 */ 534 env_get( g_env, "xwin_title", &xwin_title ); 535 if (xwin_title != NULL && *xwin_title != '\0') 536 { 537 exp_buf = varbuf_create( 64 ); 538 539 if (exp_buf == NULL) 540 { 541 fprintf( stderr, "sqsh: %s\n", sqsh_get_errstr() ); 542 sqsh_exit( 255 ); 543 } 544 545 if (sqsh_expand( xwin_title, exp_buf, 0 ) == False) 546 { 547 fprintf( stderr, "sqsh: Error expanding $xwin_title: %s\n", 548 sqsh_get_errstr() ); 549 sqsh_exit( 255 ); 550 } 551 argc = 1; 552 argv[0] = varbuf_getstr( exp_buf ); 553 } 554 else 555 { 556 argc = 0; 557 argv[0] = NULL; 558 } 559 560 /*-- Intialize our X Session --*/ 561 w_top = XtInitialize( "sqsh", "Sqsh", NULL, 0, &argc, argv ); 562 563 w_form = XtCreateManagedWidget("form", formWidgetClass, w_top, NULL, 0); 564 565 w_text = XtVaCreateManagedWidget( "text", asciiTextWidgetClass, w_form, 566 XtNdisplayCaret, FALSE, 567 XtNtop, XtChainTop, 568 XtNvertDistance, 5, 569 XtNhorizDistance, 5, 570 XtNleft, XtChainLeft, 571 XtNright, XtChainRight, 572 XtNwidth, 200, 573 XtNheight, 100, 574 XtNscrollHorizontal, XawtextScrollAlways, 575 XtNscrollVertical, XawtextScrollAlways, 576 XtNeditType, XawtextAppend, 577 NULL ); 578 579 /*-- Now, get the AsciiText's Font structure --*/ 580 XtVaGetValues( w_text, XtNfont, &font, NULL ); 581 582 if (font != NULL) 583 { 584 text_width = ((font->max_bounds.width + 1) * width) + 5; 585 586 XtVaSetValues( w_text, 587 XtNwidth, text_width, 588 XtNheight, ((font->max_bounds.ascent + 589 font->max_bounds.descent) * height)+5, 590 NULL ); 591 } 592 else 593 { 594 /*-- Assume 7 pixel width font --*/ 595 text_width = (80 * 7) + 5; 596 } 597 598 w_lbl = XtVaCreateManagedWidget( "lines", labelWidgetClass, w_form, 599 XtNlabel, "Lines: 0 ", 600 XtNleft, XtChainLeft, 601 XtNbottom, XtChainBottom, 602 XtNjustify, XtJustifyLeft, 603 XtNfromVert, w_text, 604 NULL ); 605 606 w_cmd = XtVaCreateManagedWidget( "cancel_button",commandWidgetClass,w_form, 607 XtNlabel, "Cancel", 608 XtNleft, XtRubber, 609 XtNbottom, XtChainBottom, 610 XtNfromVert, w_text, 611 XtNresizable, FALSE, 612 XtNhorizDistance, ((text_width/2) - 25), 613 NULL ); 614 615 /* 616 * The command button callback will receive a cb_data_t structure 617 * which will describe enough information to figure out how to 618 * cancel the current result set. 619 */ 620 XtAddCallback( w_cmd, XtNcallback, dsp_x_ok_cb, &cd ); 621 622 id = XtAddInput( fd, (XtPointer)XtInputReadMask, 623 (XtInputCallbackProc)dsp_x_input_cb, (XtPointer)&cd ); 624 625 /* 626 * We have already attached a cd_data_t to all of our callbacks, so 627 * now we need to populate it with enough info to get work done. 628 */ 629 cd.c_fd = fd; 630 cd.c_lines = 0; 631 cd.c_text = w_text; 632 cd.c_cmd = w_cmd; 633 cd.c_lbl = w_lbl; 634 cd.c_id = id; 635 636 XtRealizeWidget( w_top ); 637 XtMainLoop(); 638 639 if (exp_buf != NULL) 640 varbuf_destroy( exp_buf ); 641 exit(0); 642 } 643 644 static void dsp_x_ok_cb (w, client_data, call_data) 645 Widget w; 646 XtPointer client_data; 647 XtPointer call_data; 648 { 649 cb_data_t *cd; 650 char number[20]; 651 652 cd = (cb_data_t*)client_data; 653 654 if (cd->c_fd != -1) 655 { 656 sprintf( number, "Lines: %d", cd->c_lines ); 657 XtVaSetValues( cd->c_cmd, XtNlabel, "Done", NULL ); 658 XtVaSetValues( cd->c_text, XtNeditType, XawtextRead, NULL ); 659 XtVaSetValues( cd->c_lbl, XtNlabel, number, NULL ); 660 661 close( cd->c_fd ); 662 cd->c_fd = -1; 663 XtRemoveInput( cd->c_id ); 664 } 665 else 666 { 667 exit(0); 668 } 669 } 670 671 /* 672 * dsp_x_input_cb(): 673 * 674 * This function is automatically called whenever input is received 675 * from our parent process via a file descriptor. It is responsible 676 * for appending any text received onto the end of the text widget. 677 */ 678 static void dsp_x_input_cb (client_data, fd, id) 679 XtPointer client_data; 680 int *fd; 681 XtInputId *id; 682 { 683 static int len = 0; /* Total Length of text in widget */ 684 char buffer[1024]; /* Read up to 1K of input */ 685 XawTextBlock b; 686 int n; 687 cb_data_t *cd; 688 char *cp; 689 char number[20]; 690 691 cd = (cb_data_t*)client_data; 692 693 /*-- Read input from file descriptor --*/ 694 n = read( *fd, buffer, sizeof(buffer) - 1 ); 695 696 /*-- On error or EOF close connection --*/ 697 if (n <= 0) 698 { 699 sprintf( number, "Lines: %d", cd->c_lines ); 700 XtVaSetValues( cd->c_cmd, XtNlabel, "Done", NULL ); 701 XtVaSetValues( cd->c_text, XtNeditType, XawtextRead, NULL ); 702 XtVaSetValues( cd->c_lbl, XtNlabel, number, NULL ); 703 704 close( *fd ); 705 cd->c_fd = -1; 706 XtRemoveInput( cd->c_id ); 707 } 708 else 709 { 710 /*-- Append data read to the end --*/ 711 b.firstPos = 0; 712 b.length = n; 713 b.ptr = buffer; 714 b.format = FMT8BIT; 715 716 cp = buffer; 717 while ((cp = strchr( cp, '\n' )) != NULL) 718 { 719 ++cd->c_lines; 720 721 if ((cd->c_lines % 100) == 0) 722 { 723 sprintf( number, "Lines: %d", cd->c_lines ); 724 XtVaSetValues( cd->c_lbl, XtNlabel, number, NULL ); 725 } 726 727 cp += 1; 728 } 729 730 XawTextReplace( cd->c_text, len, len, &b ); 731 732 len += n; 733 } 734 } 735 736 #endif /* !USE_MOTIF */ 737 #endif /* USE_X11 */ 738 739