On Saturday 11 June 2005 11:32, Dinh Duong Ha wrote:
>
> Ky~ thua^.t ma` /me du`ng la` cha(.n signal SGN_CHILD, trong ha`m xu+?
> li' signal na`y, go.i ha`m wait dde^? cho+` child process exit.
> The^m: Ne^'u parent process ke^'t thu'c tru+o+'c child process thi`
> child process se~ co' parent process la` 1- init process, va` init
> process se~ gia?i pho'ng tho^ng tin ve^` child process khi child process
> ke^'t thu'c => gia?i pho'ng process id cho tie^'n tri`nh kha'c.
>
Xin cha`o ba'c. Ca'm o+n ba'c dda~ go+.i y'. Em dda~ la`m ddu+o+.c... su+?
du.ng signal SGN_CHILD. Tuy nhie^n, va^~n sinh ra tru.c tra(.c kha'c: ddo'
la` ddoi khi child_process thoa't , nhu+ng winefish ko^ the^? gia?i pho'ng
resource du+o+.c.
* tru+o+'c khi cha.y child_process: resource chie^'m bo+?i winefish la` 0%
* khi cha.y child_process: winefish chie^'m 0%, child chie^'m khoa?ng 30% ta`i
nguyene
* sau khi child_process ke^'t thu'c (kho^ng bie^'t ke^' thu'c kie^?u gi`),
winefish chie^'m 30% ta`i nguye^n <=== lo^~i.
Y' cu?a em la`: ta`i nguye^n cu?a tie^'n tri`nh con ddu+o+.c gia?i pho'ng
the^' na`o, trong hai tru+o+`ng ho+.p:
* tie^'n tri`nh con thoa't bi`nh thu+o+`ng?
* tie^'n tri`nh con bi. gu+?i signal SIGTERM?
Xem ta^.p tin ddi'nh ke`m.
Ca'm o+n ba'c ra^'t nhie^`u.
--
kyanh [ http://kyanh.dotgeek.org/ ]
/* $Id: outputbox.c,v 1.3 2005/07/04 03:30:56 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 <[EMAIL PROTECTED]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define DEBUG */
#include <gtk/gtk.h>
#include <sys/types.h>
#include <signal.h> /* kill() */
#include <sys/stat.h> /* open() */
#include <fcntl.h> /* kyanh, open() */
/* kyanh, 20050301,
Thanks to M. Garoche <[EMAIL PROTECTED]>
Move from <wait.h> to <sys/wait.h>
*/
#include <sys/wait.h> /* wait(), waitid() */
#include <regex.h>
#include <stdlib.h>
#include <string.h> /* strlen() */
/* Using waitpid() to free child process's resources,
we don't need anymore <signal.h>. Anyway, thanks to
M. Garoche for reporting a problem with Mac OS */
#include "bluefish.h"
#include "outputbox.h" /* myself */
#include "bf_lib.h"
#include "document.h"
#include "gtk_easy.h"
#include "stringlist.h"
#include "pixmap.h"
static void ob_lview_row_activated_lcb( GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, Toutputbox *ob )
{
GtkTreeIter iter;
gchar *file, *line;
gint lineval;
gtk_tree_model_get_iter( GTK_TREE_MODEL( ob->lstore ), &iter, path );
gtk_tree_model_get( GTK_TREE_MODEL( ob->lstore ), &iter, 0, &file, 1, &line, -1 );
g_print( "ob_lview_row_activated_lcb, file=%s, line=%s\n", file, line );
if ( file && strlen( file ) ) {
doc_new_with_file( ob->bfwin, file, FALSE, FALSE );
}
if ( line && strlen( line ) ) {
lineval = atoi( line );
flush_queue();
doc_select_line( ob->bfwin->current_document, lineval, TRUE );
}
g_free( line );
g_free( file );
}
static void outputbox_close_clicked_lcb( GtkWidget *widget, Toutputbox *ob )
{
setup_toggle_item_from_widget( ob->bfwin->menubar, N_( "/View/View Outputbox" ), FALSE );
gtk_widget_hide( ob->hbox );
}
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 GtkWidget *create_popup_menu ()
{
GtkWidget *menu;
GtkWidget *menu_item;
menu = gtk_menu_new ();
/ * Add the separator * /
menu_item = gtk_separator_menu_item_new ();
gtk_widget_show (menu_item);
gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
return menu;
}
*/
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 */
GtkTreeViewColumn * column;
GtkWidget *scrolwin;
GtkCellRenderer *renderer;
GtkWidget *vbox2, *but, *image;
Toutputbox *ob;
ob = g_new0( Toutputbox, 1 );
DEBUG_MSG( "init_output_box, created %p\n", ob );
bfwin->outputbox = ob;
ob->bfwin = bfwin;
ob->def = NULL;
ob->pid=0;
ob->hbox = gtk_hbox_new( FALSE, 0 );
gtk_paned_add2( GTK_PANED( bfwin->vpane ), ob->hbox );
gtk_paned_set_position( GTK_PANED( bfwin->vpane ), ( gint ) ( bfwin->vpane->allocation.height * 0.8 ) );
ob->lstore = gtk_list_store_new ( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
ob->lview = gtk_tree_view_new_with_model( GTK_TREE_MODEL( ob->lstore ) );
g_object_unref( ob->lstore );
/* the view widget now holds the only reference, if the view is destroyed, the model will be destroyed */
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes ( _("Filename"), renderer, "text", 0, NULL );
gtk_tree_view_append_column ( GTK_TREE_VIEW( ob->lview ), column );
column = gtk_tree_view_column_new_with_attributes ( _("Line"), renderer, "text", 1, NULL );
gtk_tree_view_append_column ( GTK_TREE_VIEW( ob->lview ), column );
column = gtk_tree_view_column_new_with_attributes ( _("Output"), renderer, "markup", 2, NULL );
gtk_tree_view_append_column ( GTK_TREE_VIEW( ob->lview ), column );
scrolwin = gtk_scrolled_window_new( NULL, NULL );
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolwin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
gtk_container_add( GTK_CONTAINER( scrolwin ), ob->lview );
gtk_widget_set_size_request( scrolwin, 150, 150 );
gtk_box_pack_start( GTK_BOX( ob->hbox ), scrolwin, TRUE, TRUE, 0 );
g_signal_connect( G_OBJECT( ob->lview ), "row-activated", G_CALLBACK( ob_lview_row_activated_lcb ), ob );
vbox2 = gtk_vbox_new( FALSE, 0 );
but = gtk_button_new();
image = new_pixmap( 4 );
gtk_widget_show( image );
gtk_container_add( GTK_CONTAINER( but ), image );
gtk_container_set_border_width( GTK_CONTAINER( but ), 0 );
gtk_widget_set_size_request( but, 16, 16 );
g_signal_connect( G_OBJECT( but ), "clicked", G_CALLBACK( outputbox_close_clicked_lcb ), ob );
gtk_box_pack_start( GTK_BOX( ob->hbox ), vbox2, FALSE, FALSE, 0 );
gtk_box_pack_start( GTK_BOX( vbox2 ), but, FALSE, FALSE, 0 );
gtk_widget_show_all( ob->hbox );
setup_toggle_item_from_widget( bfwin->menubar, N_( "/View/View Outputbox" ), TRUE );
/* create the popup menu */
/* GtkWidget * menu = create_popup_menu(); */
return ob;
}
/* kyanh, added, 20050227
Send some specific message to outputbox.
Please DONOT use for general purpose :!
@markup: should be: b, i, u
*/
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);
} else if ( regexec( &ob->def->preg, source, NUM_MATCH, ob->def->pmatch, 0 ) == 0 ) {
/* we have a valid line */
gchar * filename, *line, *output;
filename = line = output = NULL;
gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter );
if ( ob->def->file_subpat >= 0 && ob->def->pmatch[ ob->def->file_subpat ].rm_so >= 0 ) {
DEBUG_MSG( "fill_outputbox, filename from %d to %d\n", ob->def->pmatch[ ob->def->file_subpat ].rm_so , ob->def->pmatch[ ob->def->file_subpat ].rm_eo );
filename = g_strndup( &source[ ob->def->pmatch[ ob->def->file_subpat ].rm_so ], ob->def->pmatch[ ob->def->file_subpat ].rm_eo - ob->def->pmatch[ ob->def->file_subpat ].rm_so );
}
if ( ob->def->line_subpat >= 0 && ob->def->pmatch[ ob->def->line_subpat ].rm_so >= 0 ) {
DEBUG_MSG( "fill_outputbox, line from %d to %d\n", ob->def->pmatch[ ob->def->line_subpat ].rm_so , ob->def->pmatch[ ob->def->line_subpat ].rm_eo );
line = g_strndup( &source[ ob->def->pmatch[ ob->def->line_subpat ].rm_so ], ob->def->pmatch[ ob->def->line_subpat ].rm_eo - ob->def->pmatch[ ob->def->line_subpat ].rm_so );
}
if ( ob->def->output_subpat >= 0 && ob->def->pmatch[ ob->def->output_subpat ].rm_so >= 0 ) {
DEBUG_MSG( "fill_outputbox, output from %d to %d\n", ob->def->pmatch[ ob->def->output_subpat ].rm_so , ob->def->pmatch[ ob->def->output_subpat ].rm_eo );
output = g_strndup( &source[ ob->def->pmatch[ ob->def->output_subpat ].rm_so ], ob->def->pmatch[ ob->def->output_subpat ].rm_eo - ob->def->pmatch[ ob->def->output_subpat ].rm_so );
}
if ( filename ) {
gchar * fullpath;
/* create_full_path uses the current directory of no basedir is set */
/* TODO: better hanlder with full path :) */
fullpath = create_full_path( filename, NULL );
gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 0, fullpath, -1 );
g_free( filename );
g_free( fullpath );
}
if ( line ) {
gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 1, line, -1 );
g_free( line );
}
if ( output ) {
tmp_src = g_markup_escape_text(output,-1);
gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmp_src, -1 );
g_free( output );
g_free(tmp_src);
}
/* TODO: filter function */
} else if ( strstr( source, "LaTeX Error" )
|| strstr( source, "Output written on")
|| strstr( source, "Warning")
|| strstr( source, "Overfull")
)
{
/* kyanh, 20050226,
special filter for latex *.log file;
this happens for e.g. when a Package not found (! LaTeX Error: .... )
*/
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;
}
/* kyanh, 20050302, moved from continute_execute() */
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
Modified by [EMAIL PROTECTED]
kyanh, version 2 (20050302): use g_io_channel()
kyanh, version 2.1 (20050315): readline(), not readchars()
*/
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 );
}
/* continue to read data */
/* written by kyanh, 20050301 */
static void io_signal( GIOChannel *source, GIOCondition condition, Toutputbox *ob )
{
continue_execute( ob );
}
/* Taken from SciTTEGTK.cxx
Detect if the tool has exited without producing any output
*/
static int poll_tool( Toutputbox *ob )
{
continue_execute( ob );
return TRUE;
}
/* Taken from SciTTEGTK.cxx
kyanh: everything emitted from `running' will be captured [ 2>&1 ]
*/
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 ); /* dup uses the lowest?numbered unused descriptor for the new descriptor. */
close( 2 );
dup( fh );
DEBUG_MSG( "xsystem: running now [%s]\n", command );
execlp( "/bin/sh", "sh", "-c", command, NULL );
/* Functions that contain the letter l (execl, execlp, and
execle) accept the argument list using the C language varargs mechanism. */
/* The execvp function returns only if an error occurs. */
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); */
}