Hi all, I currently maintain winefish [ http://winefish.berlios.de/ ] >From winefish, some external tools (latex, pdflatex,...) will be excuted. The output of theses tools are expected to be appear line-by-line in the outputbox (GUI). But when a tool is called, Winefish is suspend until the tool finishes, then the lines of output appear (all at once, NOT line by line as desire). I tried but didn't understand what the reason is. Please read the code below and give me some advice. Thank you very much. Regards, -- kyanh [ http://kyanh.dotgeek.org/ ] PS: [code] /* $Id: outputbox.c,v 1.2 2005/07/02 08:28:49 kyanh Exp $ */ /* Winefish LaTeX Editor (based on Bluefish HTML Editor) * outputbox.c the output box * * Copyright (C) 2002 Olivier Sessink * Modified for Winefish (C) 2005 Ky Anh <kyanh@xxxxx> * License: GPL */ /* the header */ static void ob_lview_row_activated_lcb( GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, Toutputbox *ob ) {...} static void outputbox_close_clicked_lcb( GtkWidget *widget, Toutputbox *ob ) {...} static sig_atomic_t child_exit_status; static void clean_up_child_process (gint signal_number) { /* Clean up the child process. */ gint status; wait (&status); /* Store its exit status in a global variable. */ child_exit_status = status; } static Toutputbox *init_outputbox( Tbfwin *bfwin ) { /* we call this once */ /* Handle SIGCHLD by calling clean_up_child_process. */ struct sigaction sigchld_action; memset (&sigchld_action, 0, sizeof (sigchld_action)); sigchld_action.sa_handler = &clean_up_child_process; sigaction (SIGCHLD, &sigchld_action, NULL); /* create the backend ... */ return ob; } static void outputbox_message( Toutputbox *ob, const char *string, const char *markup ) { GtkTreeIter iter; gchar *tmpstr = g_markup_escape_text(string,-1); if (markup) { tmpstr = g_strdup_printf("> <%s>%s</%s>", markup, string, markup); }else{ tmpstr = g_strdup_printf("> %s", string); } gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter ); gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmpstr, -1 ); g_free(tmpstr); /* TODO: Scroll as an Optional */ /* The Outputbox may *NOT* be shown before scrolling :) */ /* kyanh, added, 20050301 */ GtkTreePath *treepath = gtk_tree_model_get_path( GTK_TREE_MODEL( ob->lstore ), &iter ); gtk_tree_view_scroll_to_cell( GTK_TREE_VIEW( ob->lview ), treepath, NULL, FALSE /* skip align */, 0, 0 ); gtk_tree_path_free( treepath ); } static void fill_outputbox( Toutputbox *ob, const gchar *source ) { GtkTreeIter iter; gchar *tmp_src = NULL; if ( ob->def->show_all_output ) { tmp_src = g_markup_escape_text(source,-1); gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter ); gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmp_src, -1 ); g_free(tmp_src); } } /* kyanh, added, 20050301 */ static void free_ob( Toutputbox *ob, gboolean have_retfile ) { if ( have_retfile ) { /* free temporarily file */ remove_secure_dir_and_filename( ob->retfile ); DEBUG_MSG( "continue_execute: retfile=%s\n", ob->retfile ); g_free( ob->retfile ); } g_free( ob->def->pattern ); regfree( &ob->def->preg ); g_free( ob->def->command ); g_free( ob->def ); ob->def = NULL; /* to be check for next using */ ob->pid = 0; } static void finish_execute( Toutputbox *ob ) { kill( ob->pid, SIGTERM ); waitpid( ob->pid, &child_exit_status, WNOHANG ); { gint exitcode = WEXITSTATUS( child_exit_status ); gchar *str_status = g_strdup_printf(_("Exit code: %d"), exitcode); outputbox_message( ob, str_status, "b" ); g_free( str_status ); } gtk_tree_view_columns_autosize( GTK_TREE_VIEW( ob->lview ) ); g_io_channel_unref( ob->io_channel ); gtk_timeout_remove( ob->pollID ); free_ob( ob, 1 ); } /* Idea taken from SciTTEGTK.cxx */ static void continue_execute( Toutputbox *ob ) { gsize count = 0; GIOStatus io_status; GError *error = NULL; gchar *buf = NULL; gsize terminator_pos = 0; gboolean continued = TRUE; while ( continued ) { continued = FALSE; buf = NULL; io_status = g_io_channel_read_line( ob->io_channel, &buf, &count, &terminator_pos, &error ); switch ( io_status ) { case G_IO_STATUS_ERROR: { gchar * tmpstr; tmpstr = g_strdup_printf( _("IOChannel Error: %s"), error->message ); outputbox_message( ob, tmpstr, "b" ); g_free( tmpstr ); finish_execute( ob ); } break; case G_IO_STATUS_EOF: /* without this, we dump into an infinite loop */ finish_execute( ob ); break; case G_IO_STATUS_NORMAL: continued = TRUE; if ( terminator_pos < count ) { buf[ terminator_pos ] = '\0'; } fill_outputbox( ob, buf ); break; default: break; } } g_free( buf ); g_clear_error( &error ); } static void io_signal( GIOChannel *source, GIOCondition condition, Toutputbox *ob ) { continue_execute( ob ); } static int poll_tool( Toutputbox *ob ) { continue_execute( ob ); return TRUE; } static gint xsystem( const gchar *command, const gchar *outfile ) { gint pid = 0; /* fork(): create a child proccess the differs from the parent only in its PID and PPID; the resouce ultilisation are set to 0 */ if ( ( pid = fork() ) == 0 ) { close( 0 ); gint fh = open( outfile, O_WRONLY ); close( 1 ); dup( fh ); close( 2 ); dup( fh ); DEBUG_MSG( "xsystem: running now [%s]\n", command ); execlp( "/bin/sh", "sh", "-c", command, NULL ); exit( 127 ); } /* This is the parent process. */ return pid; } static void run_command( Toutputbox *ob ) { file_save_cb( NULL, ob->bfwin ); outputbox_message( ob, ob->def->command, "i" ); { gchar *project_mode; if ( main_v->props.project_mode ) { project_mode = g_strdup( _("Project Mode: ON") ); } else { project_mode = g_strdup( _("Project Mode: OFF") ); } outputbox_message( ob, project_mode, "i" ); g_free( project_mode ); } if ( ob->bfwin->current_document->filename ) { /* if the user clicked cancel at file_save -> return */ { gchar * tmpstring; if ( main_v->props.project_mode && ob->bfwin->project && ob->bfwin->project->basedir ) { tmpstring = g_strdup( ob->bfwin->project->basedir ); } else { tmpstring = g_path_get_dirname( ob->bfwin->current_document->filename ); } /* outputbox_message(ob, g_strconcat("> Working dir: ", tmpstring, NULL)); */ chdir( tmpstring ); g_free( tmpstring ); } gchar *command = convert_command( ob->bfwin, ob->def->command ); outputbox_message( ob, command, "b"); ob->retfile = create_secure_dir_return_filename(); gint fd = 1; if ( ob->retfile ) { fd = mkfifo( ob->retfile, S_IRUSR | S_IWUSR ); if ( fd == 0 ) { ob->pid = xsystem( command, ob->retfile ); GError *error = NULL; ob->io_channel = g_io_channel_new_file( ob->retfile, "r", &error ); if ( ob->io_channel != NULL ) { /* Fix the BUGS[200503]#20 */ g_io_channel_set_encoding( ob->io_channel, NULL, NULL ); g_io_add_watch( ob->io_channel, G_IO_IN, ( GIOFunc ) io_signal, ob ); /* add a background task in case there is no output from the tool */ ob->pollID = g_timeout_add( 200, ( GSourceFunc ) poll_tool, ob ); } else { gchar *tmpstr; tmpstr = g_strdup_printf( _("Error: %s"), error->message ); outputbox_message( ob, tmpstr, "b" ); if ( error->code == G_FILE_ERROR_INTR ) { outputbox_message( ob, _("Hint: You may call the tool again"), "i" ); } g_free( tmpstr ); outputbox_message( ob, _("Tool finished."), "b" ); free_ob( ob, 1 ); } g_clear_error( &error ); } } if ( fd != 0 ) { outputbox_message( ob, _("Error: Cannot create PIPE."), "b" ); free_ob( ob, 1 ); } g_free( command ); } else { outputbox_message( ob, _("Tool canceled."), "b" ); free_ob( ob, 0 ); } } void outputbox( Tbfwin *bfwin, gchar *pattern, gint file_subpat, gint line_subpat, gint output_subpat, gchar *command, gboolean show_all_output ) { Toutputbox * ob; if ( bfwin->outputbox ) { ob = OUTPUTBOX( bfwin->outputbox ); gtk_widget_show_all( ob->hbox ); /* fix BUGS[200503]#24 */ setup_toggle_item_from_widget(bfwin->menubar, N_("/View/View Outputbox"), TRUE); /* fix BUGS[200503]#25 */ } else { ob = init_outputbox( bfwin ); } if ( ob->pid ) { /* stop older output box */ outputbox_message( ob, _("Tool is running. Try 'Stop' intead."), "i" ); return; /* gchar * tmpstr; tmpstr = g_strdup_printf(_("Multiple calls... stopping: %s"), ob->def->command ); outputbox_message( ob, tmpstr, "i" ); g_free( tmpstr ); finish_execute( ob ); */ } gtk_list_store_clear( GTK_LIST_STORE( ob->lstore ) ); ob->def = g_new0( Toutput_def, 1 ); ob->def->pattern = g_strdup( pattern ); ob->def->file_subpat = file_subpat; ob->def->line_subpat = line_subpat; ob->def->output_subpat = output_subpat; ob->def->show_all_output = show_all_output; regcomp( &ob->def->preg, ob->def->pattern, REG_EXTENDED ); ob->def->command = g_strdup( command ); /* kyanh */ ob->retfile = NULL; ob->io_channel = NULL; ob->pollID = 0; ob->pid = 0; run_command( ob ); /* gtk_widget_show_all(ob->hbox); */ } [/code] _______________________________________________ gtk-list@xxxxxxxxx http://mail.gnome.org/mailman/listinfo/gtk-list