1 /*
2  *  xnec2c - GTK2-based version of nec2c, the C translation of NEC2
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU Library General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 /*
20  * Initial main.c file generated by Glade. Edit as required.
21  * Glade will not overwrite this file.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27 
28 #include <gtk/gtk.h>
29 #include <sys/wait.h>
30 #include <locale.h>
31 
32 #include "main.h"
33 #include "shared.h"
34 
35 static void sig_handler(int signal);
36 
37 /* Child process pid returned by fork() */
38 static pid_t child_pid = (pid_t)(-1);
39 
40 /*------------------------------------------------------------------------*/
41 
42   int
main(int argc,char * argv[])43 main (int argc, char *argv[])
44 {
45   /* getopt() variables */
46   int option, idx, err;
47   gboolean new = TRUE;
48 
49   /*** Signal handler related code ***/
50   /* new and old actions for sigaction() */
51   struct sigaction sa_new, sa_old;
52 
53   /* initialize new actions */
54   sa_new.sa_handler = sig_handler;
55   sigemptyset( &sa_new.sa_mask );
56   sa_new.sa_flags = 0;
57 
58   /* register function to handle signals */
59   sigaction( SIGINT,  &sa_new, &sa_old );
60   sigaction( SIGSEGV, &sa_new, NULL );
61   sigaction( SIGFPE,  &sa_new, NULL );
62   sigaction( SIGTERM, &sa_new, NULL );
63   sigaction( SIGABRT, &sa_new, NULL );
64   sigaction( SIGCHLD, &sa_new, NULL );
65 
66   /* Process command line options */
67   calc_data.num_jobs = 1;
68   while( (option = getopt(argc, argv, "i:j:hv") ) != -1 )
69   {
70 	switch( option )
71 	{
72 	  case 'i': /* specify input file name */
73 		if( strlen(optarg) > 80 )
74 		{
75 		  fprintf ( stderr,
76 			  _("xnec2c: input file name too long (>80 char)\n") );
77 		  exit(-1);
78 		}
79 		Strlcpy( infile, optarg, sizeof(infile) ); /* For null term. */
80 		break;
81 
82 	  case 'j': /* number of child processes = num of processors */
83 		calc_data.num_jobs = atoi( optarg );
84 		break;
85 
86 	  case 'h': /* print usage and exit */
87 		usage();
88 		exit(0);
89 
90 	  case 'v': /* print xnec2c version */
91 		puts( PACKAGE_STRING );
92 		exit(0);
93 
94 	  default:
95 		usage();
96 		exit(0);
97 
98 	} /* end of switch( option ) */
99 
100   } /* while( (option = getopt(argc, argv, "i:o:hv") ) != -1 ) */
101 
102   /* Read input file path name if not supplied by -i option */
103   if( (strlen(infile) == 0) &&
104 	  (strstr(argv[argc - 1], ".nec") || strstr(argv[argc - 1], ".NEC")) )
105   {
106 	if( strlen(argv[argc - 1]) > 80 )
107 	{
108 	  fprintf ( stderr,
109 		  _("xnec2c: input file path name too long (>80 char)\n") );
110 	  exit(-1);
111 	}
112 	Strlcpy( infile, argv[argc-1], sizeof(infile) ); /* For null term. */
113   }
114 
115   /* When forking is useful, e.g. if more than 1 processor is
116    * available, the parent process handles the GUI and delegates
117    * calculations to the child processes, one per processor. The
118    * requested number of child processes = number of processors */
119 
120   /* Allocate buffers for fork data */
121   if( calc_data.num_jobs > 1 )
122   {
123 	size_t mreq = (size_t)calc_data.num_jobs * sizeof(forked_proc_data_t *);
124 	mem_alloc( (void **)&forked_proc_data, mreq, "in main.c" );
125 	for( idx = 0; idx < calc_data.num_jobs; idx++ )
126 	{
127 	  forked_proc_data[idx] = NULL;
128 	  mreq = sizeof(forked_proc_data_t);
129 	  mem_alloc( (void **)&forked_proc_data[idx], mreq, "in main.c" );
130 	}
131 
132 	/* Fork child processes */
133 	for( idx = 0; idx < calc_data.num_jobs; idx++ )
134 	{
135 	  /* Make pipes to transfer data */
136 	  err = pipe( forked_proc_data[idx]->pnt2child_pipe );
137 	  if( err )
138 	  {
139 		perror( "xnec2c: pipe()" );
140 		fprintf( stderr,
141 			_("xnec2c: exiting after fatal error (pipe() failed)") );
142 		exit(-1);
143 	  }
144 
145 	  err = pipe( forked_proc_data[idx]->child2pnt_pipe );
146 	  if( err )
147 	  {
148 		perror( "xnec2c: pipe()" );
149 		fprintf( stderr,
150 			_("xnec2c: exiting after fatal error (pipe() failed)") );
151 		exit(-1);
152 	  }
153 
154 	  /* Fork child process */
155 	  forked_proc_data[idx]->child_pid = fork();
156 	  if( forked_proc_data[idx]->child_pid == -1 )
157 	  {
158 		perror( "xnec2c: fork()" );
159 		fprintf( stderr,
160 			_("xnec2c: exiting after fatal error (fork() failed)") );
161 		exit(-1);
162 	  }
163 	  else child_pid = forked_proc_data[idx]->child_pid;
164 
165 	  /* Child get out of forking loop! */
166 	  if( CHILD ) Child_Process( idx );
167 
168 	  /* Ready to accept a job */
169 	  forked_proc_data[idx]->busy = FALSE;
170 
171 	  /* Close unwanted pipe ends */
172 	  close( forked_proc_data[idx]->pnt2child_pipe[READ] );
173 	  close( forked_proc_data[idx]->child2pnt_pipe[WRITE] );
174 
175 	  /* Set file descriptors for select() */
176 	  FD_ZERO( &forked_proc_data[idx]->read_fds );
177 	  FD_SET( forked_proc_data[idx]->child2pnt_pipe[READ],
178 		  &forked_proc_data[idx]->read_fds );
179 	  FD_ZERO( &forked_proc_data[idx]->write_fds );
180 	  FD_SET( forked_proc_data[idx]->pnt2child_pipe[WRITE],
181 		  &forked_proc_data[idx]->write_fds );
182 
183 	  /* Count child processes */
184 	  num_child_procs++;
185 	} /* for( idx = 0; idx < calc_data.num_jobs; idx++ ) */
186 
187 	FORKED = TRUE;
188   } /* if( calc_data.num_jobs > 1 ) */
189 
190   gtk_set_locale ();
191   gtk_init (&argc, &argv);
192   setlocale(LC_NUMERIC, "C");
193   add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
194 
195   /*
196    * The following code was added by Glade to create one of each component
197    * (except popup menus), just so that you see something after building
198    * the project. Delete any components that you don't want shown initially.
199    */
200   main_window = create_main_window ();
201   gtk_window_set_title( GTK_WINDOW(main_window), PACKAGE_STRING );
202   gtk_widget_show (main_window);
203   mainwin_frequency = GTK_SPIN_BUTTON(lookup_widget(
204 		main_window, "main_freq_spinbutton") );
205 
206   gtk_widget_hide_all( lookup_widget(
207 		main_window, "main_hbox1") );
208   gtk_widget_hide_all( lookup_widget(
209 		main_window, "main_hbox2") );
210   gtk_widget_hide_all( lookup_widget(
211 		main_window, "main_hbox3") );
212   gtk_widget_hide_all( lookup_widget(
213 		main_window, "main_view_menuitem") );
214   gtk_widget_hide( lookup_widget(
215 		main_window, "structure_drawingarea") );
216 
217   structure_drawingarea = lookup_widget(
218 	  main_window, "structure_drawingarea");
219   gtk_widget_add_events(
220 	  GTK_WIDGET(structure_drawingarea),
221 	  GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK );
222   structure_motion_handler = g_signal_connect (
223 	  (gpointer) structure_drawingarea,
224 	  "motion_notify_event",
225 	  G_CALLBACK (on_structure_drawingarea_motion_notify_event),
226 	  NULL);
227 
228   /* Initialize structure projection angles */
229   rotate_structure  = GTK_SPIN_BUTTON(lookup_widget(
230 		main_window, "main_rotate_spinbutton"));
231   incline_structure = GTK_SPIN_BUTTON(lookup_widget(
232 		main_window, "main_incline_spinbutton"));
233   structure_zoom = GTK_SPIN_BUTTON(lookup_widget(
234 		main_window, "structure_zoom_spinbutton"));
235   structure_fstep_entry = GTK_ENTRY(lookup_widget(
236 		main_window, "structure_fstep_entry")) ;
237 
238   structure_proj_params.Wr =
239 	gtk_spin_button_get_value(rotate_structure);
240   structure_proj_params.Wi =
241 	gtk_spin_button_get_value(incline_structure);
242 
243   structure_proj_params.xy_zoom = 1.0;
244   structure_proj_params.reset = TRUE;
245   structure_proj_params.type = STRUCTURE_DRAWINGAREA;
246 
247   rdpattern_proj_params.xy_zoom = 1.0;
248   rdpattern_proj_params.reset = TRUE;
249   rdpattern_proj_params.type = RDPATTERN_DRAWINGAREA;
250   calc_data.zo = 50.0;
251 
252   /* Open input file if specified */
253   if( strlen(infile) > 0 )
254 	g_idle_add( Open_Input_File, (gpointer)&new );
255   else
256 	SetFlag( INPUT_PENDING );
257 
258   gtk_main ();
259 
260   return 0;
261 }
262 
263 /*-----------------------------------------------------------------------*/
264 
265 /* Open_Input_File()
266  *
267  * Opens NEC2 input file
268  */
269 
270   gboolean
Open_Input_File(gpointer udata)271 Open_Input_File( gpointer udata )
272 {
273   gboolean ok, new;
274 
275   /* Stop freq loop */
276   if( isFlagSet(FREQ_LOOP_RUNNING) )
277 	Stop_Frequency_Loop();
278 
279   /* Close open files if any */
280   Close_File( &input_fp );
281 
282   /* Suppress activity while input file opened */
283   ClearFlag( OPEN_INPUT_FLAGS );
284   SetFlag( INPUT_PENDING );
285   calc_data.fstep = -1;
286 
287   /* Open NEC2 input file */
288   Open_File( &input_fp, infile, "r");
289 
290   /* Read input file, record failures */
291   ok = Read_Comments() && Read_Geometry() && Read_Commands();
292   if( !ok )
293   {
294 	/* Hide main control buttons etc */
295 	gtk_widget_hide_all( lookup_widget(
296 		  main_window, "main_hbox1") );
297 	gtk_widget_hide_all( lookup_widget(
298 		  main_window, "main_hbox2") );
299 	gtk_widget_hide_all( lookup_widget(
300 		  main_window, "main_view_menuitem") );
301 	gtk_widget_hide( lookup_widget(
302 		  main_window, "structure_drawingarea") );
303 
304 	/* Close plot/rdpat windows */
305 	if( rdpattern_window != NULL )
306 	  gtk_widget_destroy( rdpattern_window );
307 	if( freqplots_window != NULL )
308 	  gtk_widget_destroy( freqplots_window );
309 
310 	if( nec2_edit_window == NULL )
311 	  Open_Nec2_Editor( NEC2_EDITOR_RELOAD );
312 	else
313 	  Nec2_Input_File_Treeview( NEC2_EDITOR_REVERT );
314 
315 	return( FALSE );
316   }
317 
318   /* Ask child processes to read input file */
319   if( FORKED )
320   {
321 	int idx;
322 	size_t lenc, leni;
323 
324 	lenc = strlen( fork_commands[INFILE] );
325 	leni = strlen( infile );
326 	for( idx = 0; idx < num_child_procs; idx++ )
327 	{
328 	  Write_Pipe( idx, fork_commands[INFILE], (ssize_t)lenc, TRUE );
329 	  Write_Pipe( idx, infile, (ssize_t)leni, TRUE );
330 	}
331   } /* if( FORKED ) */
332 
333   /* Allow redraws on expose events etc */
334   ClearFlag( INPUT_PENDING );
335 
336   /* Initialize xnec2c */
337   SetFlag( COMMON_PROJECTION );
338   SetFlag( COMMON_FREQUENCY );
339   SetFlag( MAIN_NEW_FREQ );
340   SetFlag( FREQ_LOOP_INIT );
341   floop_tag = 0;
342   save.last_freq = 0.0;
343   crnt.newer = 0;
344   crnt.valid = 0;
345   near_field.newer = 0;
346   near_field.valid = 0;
347 
348   /* Set projection at 45 deg rotation and
349    * inclination if NEC2 editor window is not open */
350   if( nec2_edit_window == NULL )
351 	New_Viewer_Angle( 45.0, 45.0, rotate_structure,
352 		incline_structure, &structure_proj_params );
353   New_Structure_Projection_Angle();
354 
355   /* Show current frequency */
356   gtk_spin_button_set_value(
357 	  mainwin_frequency, (gdouble)calc_data.fmhz );
358 
359   /* Show main control buttons etc */
360   gtk_widget_show_all( lookup_widget(
361 		main_window, "main_hbox1") );
362   gtk_widget_show_all( lookup_widget(
363 		main_window, "main_hbox2") );
364   gtk_widget_show_all( lookup_widget(
365 		main_window, "main_hbox3") );
366   gtk_widget_show_all( lookup_widget(
367 		main_window, "main_view_menuitem") );
368   gtk_widget_show( lookup_widget(
369 		main_window, "structure_drawingarea") );
370 
371   /* If currents or charges draw button is active
372    * re-initialize structure currents/charges drawing */
373   if( isFlagSet(DRAW_CURRENTS) )
374 	Main_Currents_Togglebutton_Toggled( TRUE );
375   if( isFlagSet(DRAW_CHARGES) )
376 	Main_Charges_Togglebutton_Toggled( TRUE );
377 
378   /* Set input file to NEC2 editor. It will only
379    * happen if the NEC2 editor window is open */
380   new = *(gboolean *)udata;
381   if( new )
382 	Nec2_Input_File_Treeview( NEC2_EDITOR_REVERT );
383   else
384 	Nec2_Input_File_Treeview( NEC2_EDITOR_RELOAD );
385 
386   /* Display sructure */
387   Draw_Structure( structure_drawingarea );
388 
389   /* Re-initialize Rad Pattern drawing if window open */
390   if( rdpattern_window != NULL )
391   {
392 	Main_Rdpattern_Activate( FALSE );
393 	crnt.valid = FALSE;
394 
395 	if( isFlagSet(DRAW_GAIN) )
396 	  Rdpattern_Gain_Togglebutton_Toggled( TRUE );
397 	else
398 	  Rdpattern_Gain_Togglebutton_Toggled( FALSE );
399 
400 	if( isFlagSet(DRAW_EHFIELD) )
401 	  Rdpattern_EH_Togglebutton_Toggled( TRUE );
402 	else
403 	  Rdpattern_EH_Togglebutton_Toggled( FALSE );
404   }
405 
406   /* Re-initiate plots if window open */
407   if( freqplots_window != NULL )
408   {
409 	Main_Freqplots_Activate();
410 	Start_Frequency_Loop();
411   }
412 
413   return( FALSE );
414 } /* Open_Input_File() */
415 
416 /*------------------------------------------------------------------------*/
417 
sig_handler(int signal)418 static void sig_handler( int signal )
419 {
420   switch( signal )
421   {
422 	case SIGINT:
423 	  fprintf( stderr, _("xnec2c: exiting via user interrupt\n") );
424 	  break;
425 
426 	case SIGSEGV:
427 	  fprintf( stderr, _("xnec2c: segmentation fault, exiting\n") );
428 	  break;
429 
430 	case SIGFPE:
431 	  fprintf( stderr, _("xnec2c: floating point exception, exiting\n") );
432 	  break;
433 
434 	case SIGABRT:
435 	  fprintf( stderr, _("xnec2c: abort signal received, exiting\n") );
436 	  break;
437 
438 	case SIGTERM:
439 	  fprintf( stderr, _("xnec2c: termination request received, exiting\n") );
440 	  break;
441 
442 	case SIGCHLD:
443 	  {
444 		int idx;
445 		pid_t pid = getpid();
446 
447 		if( !FORKED )
448 		{
449 		  fprintf( stderr,
450 			  _("xnec2c: not forked, ignoring SIGCHLD from pid %d\n"), pid );
451 		  return;
452 		}
453 		else
454 		{
455 		  for( idx = 0; idx < calc_data.num_jobs; idx++ )
456 			if( forked_proc_data[idx]->child_pid == pid )
457 			{
458 			  fprintf( stderr, _("xnec2c: child process pid %d exited\n"), pid );
459 			  if( isFlagSet(MAIN_QUIT) ) return;
460 			  else break;
461 			}
462 		  return;
463 		}
464 	  }
465 
466 	default:
467 	  fprintf( stderr, _("xnec2c: default exit with signal: %d\n"), signal );
468   } /* switch( signal ) */
469 
470   /* Kill child processes */
471   if( FORKED && !CHILD )
472 	while( num_child_procs )
473 	{
474 	  num_child_procs--;
475 	  kill( forked_proc_data[num_child_procs]->child_pid, SIGKILL );
476 	}
477 
478   Close_File( &input_fp );
479 
480   if( CHILD ) _exit( 0 );
481   else exit( signal );
482 
483 } /* End of sig_handler() */
484 
485 /*------------------------------------------------------------------------*/
486 
487 /* Tests for child process */
488   gboolean
isChild(void)489 isChild(void)
490 {
491   return( child_pid == (pid_t)(0) );
492 }
493 
494 /*------------------------------------------------------------------------*/
495 
496