Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtappbuilder / src / ab / cgen_utils.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *      $XConsortium: cgen_utils.c /main/7 1996/10/02 10:56:38 drk $
25  *
26  *      @(#)cgen_utils.c        1.6 01 Jun 1994
27  *
28  *      RESTRICTED CONFIDENTIAL INFORMATION:
29  *
30  *      The information in this document is subject to special
31  *      restrictions in a confidential disclosure agreement between
32  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
33  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
34  *      Sun's specific written approval.  This document and all copies
35  *      and derivative works thereof must be returned or destroyed at
36  *      Sun's request.
37  *
38  *      Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
39  *
40  */
41
42
43 /*
44  * cgen_utils.c - utility functions for Code Generator Window and
45  *                property sheet.
46  */
47
48 #ifndef _POSIX_SOURCE
49 #define _POSIX_SOURCE 1
50 #endif
51
52 #include <sys/types.h>
53 #include <unistd.h>
54 #include <stdlib.h>
55 #include <dirent.h>
56 #include <sys/stat.h>
57 #include <time.h>
58 #include <ctype.h>
59 #include <errno.h>
60 #include <sys/stat.h>
61 #include <sys/wait.h>
62 #include <signal.h>
63 #include <assert.h>
64 #include <sys/param.h>
65 #include <stdio.h>
66 #include <X11/Xlib.h>
67 #include <Xm/Xm.h>
68 #include <Xm/List.h>
69 #include <Dt/TermPrim.h>
70 #include <Dt/Term.h>
71
72 #include <ab_private/ab.h>
73 #include <ab_private/abobj_set.h>
74 #include <ab_private/proj.h>
75 #include <ab_private/cgen.h>
76 #include <ab_private/obj_notify.h>
77 #include <ab_private/util.h>
78 #include <ab_private/trav.h>
79 #include <ab_private/strlist.h>
80
81 #include "dtbuilder.h"
82 #include "cgen_win_ui.h"
83 #include "palette_ui.h"
84 #include "cgen_props_ui.h"
85
86
87 #define INVALID_PID             ((pid_t)-1)
88 #define EXIT_SLEEP_SECONDS      (10)            /* see subprocess_exit() */
89 #define CHILD_PID_LIST_SIZE     EXIT_SLEEP_SECONDS
90
91 /* WARNING: If more dtcodegen options are added, then
92  *          MAX_CGEN_FIXED_ARGS must be incremented!
93  *
94  * MAX_CGEN_FIXED_ARGS = dtcodegen, -changed, -nomerge, -v or -s,
95  * -main, -p, <project_name>, NULL, +2(extra) = 10
96  */
97 #define MAX_CGEN_FIXED_ARGS     10
98
99 typedef enum
100 {
101     CG_STATUS_UNDEF = 0,
102     CG_STATUS_ERROR,
103     CG_STATUS_EXITED,
104     CG_STATUS_SIGNALLED,
105     CG_STATUS_STARTED,
106     CG_STATUS_NUM_VALUES
107 } CG_STATUS;
108
109
110 CGenOptions             CodeGenOptions;
111 StringList          user_env_vars = NULL;
112 StringList          module_list = NULL;
113 static CG_GOAL      user_goal = CG_GOAL_UNDEF;
114
115 static XtInputId    input_proc_id = (XtInputId)-1;
116 static int          status_pipe_read = -1;
117 static int          status_pipe_write = -1;
118 static Widget       output_termWidget = NULL;
119 static Widget       input_termWidget = NULL;
120 static Widget       make_run_item = NULL;
121 static Widget       gen_code_item = NULL;
122 static Widget       make_item = NULL;
123 static Widget       run_item = NULL;
124 static Widget       abort_item = NULL;
125 static Widget       cgen_props_item = NULL;
126 static Widget       gen_code_button = NULL;
127 static Widget       make_run_button = NULL;
128 static Widget       make_button = NULL;
129 static Widget       run_button = NULL;
130 static Widget       abort_button = NULL;
131 static Widget       cur_dir_text = NULL;
132 static ISTRING      termSlaveDeviceName = NULL;
133 static pid_t            child_pid_list[CHILD_PID_LIST_SIZE];
134 static pid_t            actual_process_pgid = INVALID_PID;
135 static pid_t            abortingPID = INVALID_PID;
136
137 /*
138  * Places to find various commands
139  */
140 static STRING   dtcodegenCmdList[] = 
141 {
142     "",                         /* exe-dir filled in at run time */
143     "",                         /* exe-dir/../abmf : filled in at runtime */
144     "dtcodegen", 
145     "/usr/dt/bin/dtcodegen", 
146     NULL
147 };
148
149 static STRING   makeCmdList[] =
150 {
151     "make",
152     "/usr/ccs/bin/make",
153     "/usr/dist/exe/make",
154     "/usr/dist/local/exe/make",
155     NULL
156 };
157
158
159 static BOOL     cgen_inited = FALSE;
160 int             cgen_init(void);
161 #define cgen_check_init() (cgen_inited? 0:cgen_init())
162
163 static int      util_fdsync(int fd);            /* REMIND: move to libAButil*/
164 static int      print_to_term(STRING msg);
165 static int      send_output_to_term(void);
166 static int      term_execute_command(CG_SUBCOMMAND cmd_code, STRING cmd, STRING argv[]);
167 static int      get_slave_device_name(void);
168 static int      print_failure_message(CG_SUBCOMMAND cmd_code, int exit_code);
169 static int      print_internal_err_message(void);
170 static int      print_success_message(void);
171 static int      print_cmd_not_found_message(STRING cmd);
172 static int      print_death_message(void);
173 static int      print_exit_message(int exit_code);
174 static int      print_abort_message(void);
175 static int      create_status_pipe(void);
176 static int      destroy_status_pipe(void);
177 static int      write_to_status_pipe(
178                         CG_STATUS       status_code, 
179                         CG_SUBCOMMAND   cmd_code, 
180                         void            *status_data    
181                 );
182 static int      read_from_status_pipe(
183                         CG_STATUS       *status_code_out,
184                         CG_SUBCOMMAND   *cmd_code_out,
185                         void            **status_data_out
186                 );
187 static int      careful_kill_group(pid_t pgid);
188 static int      goto_ready_state(void);
189 static int      goto_busy_state(void);
190 static int      cgenP_abort(void);
191 static int      cgen_set_title(STRING projectName);
192 static int      cgen_set_project_dir(STRING dir);
193 static int      cgen_obj_name_changed_cb(ObjEvAttChangeInfo evInfo);
194 static void     cgen_abort_at_exit(void);
195 static STRING   cgenP_get_env_var(STRING varName);
196 static int      cgenP_put_env_var(STRING varName, STRING varValue);
197 static int      set_props_proj_name(STRING);
198 static int      obj_renamedOCB(ObjEvAttChangeInfo);
199 static int      obj_destroyedOCB(ObjEvDestroyInfo);
200 static int      obj_updateOCB(ObjEvUpdateInfo info);
201 static int      do_user_action(
202                         CG_GOAL         goal,
203                         CG_SUBCOMMAND   cmd
204                 );
205 static int      build_dtcodegen_cmd_list(STRING *cmdList);
206 static int      build_dtcodegen_arg_list(STRING *argList, int *iInOut);
207 static int      check_path(void);
208 static int      check_path_to_cmd(STRING *cmdList, BOOL *allowWarnUserInOut);
209 static int      check_makefile(BOOL *continueOut);
210 static int      cgenP_makefile_is_for_project(STRING fileName, ABObj project);
211 static BOOL     strings_exist_in_file(StringList strings, FILE *file);
212 static int      destroy_makefile(void);
213 static int      destroy_links_to_file(STRING fileName);
214 static int      move_file_to_backup(STRING fileName);
215 static int      add_obj_file_name(
216                         StringList      fileNames, 
217                         ABObj           obj, 
218                         STRING          suffix
219                 );
220 static BOOL     all_files_exist(ABObj project);
221 static int      select_command(STRING *cmdList, STRING *cmdOut);
222 static BOOL     command_exists(STRING cmd, STRING path);
223 static int      save_done_cb(int status);
224 static int      add_child_to_list(pid_t child_pid);
225 static int      wait_for_child(void);
226 static STRING   cvt_type_to_ident(
227                         STRING type, 
228                         STRING identBuf, 
229                         int identBufSize
230                 );
231 static Boolean  path_is_executable(
232                         char    *path,
233                         uid_t   euid,
234                         gid_t   egid
235                 );
236
237 static void     popdown_cgen_window(
238                     Widget      widget,
239                     XtPointer   client_data,
240                     XtPointer   call_data
241                 );
242
243 static void     pipe_data_ready_proc(
244                     XtPointer   client_data,
245                     int         *fid,
246                     XtInputId   *id
247                 );
248
249 static int      subprocess_exit(int exit_code);
250
251 static int          exec_generate_code(void);
252 static int          exec_make(void);
253 static int          exec_run(void);
254
255 static int          exec_generate_proj(void);
256 static int          exec_generate_main(void);
257 static int          exec_generate_specific_files(void);
258 static int          exec_generate_specific_files_and_main(void);
259
260 #define util_fdclose(fd) \
261         ((fd) < 0? \
262             0 \
263         : \
264             ((close(fd) == 0)?  \
265                 (((fd) = -1),0) \
266             : \
267                 -1)  \
268         )
269
270 /*
271  * Exit cmd_code and exit_code are the command that was previously run
272  * and its exit status.
273  */
274 static int      exec_next_command(
275                         CG_SUBCOMMAND cmd_code, int exit_code);
276 static int      exec_next_command_for_gen_code(
277                         CG_SUBCOMMAND cmd_code, int exit_code);
278 static int      exec_next_command_for_make(
279                         CG_SUBCOMMAND cmd_code, int exit_code);
280 static int      exec_next_command_for_run(
281                         CG_SUBCOMMAND cmd_code, int exit_code);
282 static int      exec_next_command_for_build_and_run(
283                         CG_SUBCOMMAND cmd_code, int exit_code);
284 static int      exec_first_build_and_run_command(void);
285
286 /*
287  * This should go at the beginning of all public entry points. It
288  * checks to see if the request can be processed.
289  */
290 #define public_entry_point() \
291             if (AB_cgen_win == NULL) {return 0;} else {cgen_check_init();}
292
293
294 /*************************************************************************
295  **                                                                     **
296  **             PUBLIC ENTRY POINTS                                     **
297  **                                                                     **
298  *************************************************************************/
299
300 void
301 cgen_show_codegen_win(
302     void
303 )
304 {
305     Widget shell = (Widget) NULL;
306     cgen_check_init();
307
308     if (AB_cgen_win == (Widget) NULL)
309     {
310         /*
311          * Module initialization
312          */
313         dtbCgenWinMainwindowInfo_clear(&dtb_cgen_win_mainwindow);
314
315         dtb_cgen_win_mainwindow_initialize(
316                 &dtb_cgen_win_mainwindow, dtb_get_toplevel_widget());
317         AB_cgen_win = dtb_cgen_win_mainwindow.mainwindow_mainwin;
318         shell = dtb_cgen_win_mainwindow.mainwindow;
319  
320         /* Set up local handles for important widgets */
321         make_run_item    = dtb_cgen_win_mainwindow.menubar_File_item_file_pulldown_items.Make_Run_item;
322         gen_code_item    = dtb_cgen_win_mainwindow.menubar_File_item_file_pulldown_items.Generate_Code_item;
323         make_item        = dtb_cgen_win_mainwindow.menubar_File_item_file_pulldown_items.Make_item;
324         run_item         = dtb_cgen_win_mainwindow.menubar_File_item_file_pulldown_items.Run_item;
325         abort_item       = dtb_cgen_win_mainwindow.menubar_File_item_file_pulldown_items.Abort_item;
326         cgen_props_item  = dtb_cgen_win_mainwindow.menubar_Options_item_options_pulldown_items.Generator_item;
327         cur_dir_text     = dtb_cgen_win_mainwindow.proj_dir;
328         gen_code_button  = dtb_cgen_win_mainwindow.gen_code_btn;
329         make_button      = dtb_cgen_win_mainwindow.make_btn;
330         run_button       = dtb_cgen_win_mainwindow.run_btn;
331         make_run_button  = dtb_cgen_win_mainwindow.make_run_btn;
332         abort_button     = dtb_cgen_win_mainwindow.abort_btn;
333         output_termWidget= dtb_cgen_win_mainwindow.output_termp;
334         input_termWidget = dtb_cgen_win_mainwindow.input_termp;
335  
336         /* 
337          * Setup window to participate in dtbuilder window protocol
338          */  
339         ab_register_window(AB_cgen_win, AB_WIN_WINDOW, WindowHidden, 
340                 AB_toplevel, AB_WPOS_STACK_CENTER,
341                 popdown_cgen_window, NULL); 
342
343         cgen_notify_new_project(proj_get_project());
344         obj_add_rename_callback( cgen_obj_name_changed_cb,
345                                 "cgen_obj_name_changed_cb"); 
346
347         XtRealizeWidget(shell);
348         cgenP_sync_up_dir();
349         build_dtcodegen_cmd_list(dtcodegenCmdList);
350
351         /*
352          * Make sure the application being run can find its resource file
353          * By setting this via cgenP_put_env_var(), the user has the
354          * option of resetting it to its original value.
355          */
356         if (cgenP_get_env_var("XAPPLRESDIR") == NULL)
357         {
358             cgenP_put_env_var("XAPPLRESDIR", ".");
359         }
360
361         atexit(cgen_abort_at_exit);     /* make sure children die! */
362     }
363     ab_show_window(AB_cgen_win);
364
365     /* Turn off the cursor for the output only termpane */
366     DtTermDisplaySend(output_termWidget, (unsigned char *)"\e[?25l", strlen("\e[?25l"));
367 }
368
369
370 int
371 cgen_init(void)
372 {
373     int         i = 0;
374
375     cgen_inited = TRUE;
376
377     for (i = 0; i < XtNumber(child_pid_list); ++i)
378     {
379         child_pid_list[i] = INVALID_PID;
380     }
381     return 0;
382 }
383
384
385 static int
386 build_dtcodegen_cmd_list(STRING *cmdList)
387 {
388     char        exeDirCmd[MAXPATHLEN+1];
389     STRING      exeDir = NULL;
390     *exeDirCmd = 0;
391
392     exeDir = dtb_get_exe_dir();
393     if (exeDir == NULL)
394     {
395         return ERR_INTERNAL;
396     }
397
398     if (*(cmdList[0]) == 0)
399     {
400         sprintf(exeDirCmd, "%s/dtcodegen", exeDir);
401         cmdList[0] = strdup(exeDirCmd);
402     }
403
404     if (*(cmdList[1]) == 0)
405     {
406         sprintf(exeDirCmd, "%s/../abmf/dtcodegen", exeDir);
407         cmdList[1] = strdup(exeDirCmd);
408     }
409
410     return 0;
411 }
412
413
414 static void
415 popdown_cgen_window(
416     Widget      widget,
417     XtPointer   client_data,
418     XtPointer   call_data
419 )
420 {
421     /* If its secondary dialogs are open, close them first to
422      * ensure their state is recorded properly.
423      */
424     if (AB_cgen_prop_dialog && ab_window_is_open(AB_cgen_prop_dialog))
425         ui_win_show(AB_cgen_prop_dialog, False, XtGrabNone);
426     if (AB_cgen_env_dialog && ab_window_is_open(AB_cgen_env_dialog))
427         ui_win_show(AB_cgen_env_dialog, False, XtGrabNone);
428
429     ui_win_show(widget, False, XtGrabNone);
430 }
431
432
433 int     
434 cgen_notify_new_project(ABObj project)
435 {
436     public_entry_point();       /* see if we'll allow this entry */
437     cgen_set_title(project == NULL? NULL : obj_get_name(project));
438     cgen_set_project_dir(NULL);
439     return 0;
440 }
441
442
443 int
444 cgen_notify_new_directory(STRING dir)
445 {
446     public_entry_point();
447     cgen_set_project_dir(dir);
448     cgenP_sync_up_dir();        /* NOTE: assuming dir = project dir */
449     return 0;
450 }
451
452
453 int
454 cgen_abort()
455 {
456     public_entry_point();
457     return cgenP_abort();
458 }
459
460
461 static void
462 cgen_abort_at_exit(void)
463 {
464     cgen_abort();
465 }
466
467 int
468 cgen_notify_props_new_proj(
469     ABObj project
470 )
471 {
472     int         ret = 0;
473
474     ret = set_props_proj_name(project == NULL? NULL : obj_get_name(project));
475     return ret;
476 }
477  
478 void
479 cgenP_init_props_module_list(
480     Widget      mod_list
481 )
482 {
483     ABObj               proj = proj_get_project();
484     ABObj               obj = NULL;
485     AB_TRAVERSAL        trav;
486  
487     for (trav_open(&trav, proj, AB_TRAV_MODULES);
488         (obj= trav_next(&trav)) != NULL; )
489     {
490         if (obj_is_defined(obj))
491         {
492             ui_list_add_item(mod_list, obj_get_name(obj), 0);
493         }
494     }
495     trav_close(&trav);
496 }    
497  
498 /*
499  * Initialize Code Generator Props:
500  *      Add callbacks for object rename & destroy
501  */
502 void
503 cgenP_prop_init(
504 )
505 {
506     obj_add_rename_callback(obj_renamedOCB, "cgen_prop_init");
507     obj_add_update_callback(obj_updateOCB, "cgen_prop_init");
508     obj_add_destroy_callback(obj_destroyedOCB, "cgen_prop_init");
509 }
510  
511 void
512 cgen_gen_code(
513     CG_SUBCOMMAND       cmd
514 )
515 {
516     do_user_action(CG_GOAL_GEN_CODE, cmd);
517 }
518
519 void
520 cgen_make(
521     CG_SUBCOMMAND       cmd
522 )
523 {
524     do_user_action(CG_GOAL_MAKE , cmd);
525 }
526
527 void
528 cgen_run(
529     CG_SUBCOMMAND       cmd
530 )
531 {
532     do_user_action(CG_GOAL_RUN, cmd);
533 }
534
535 void
536 cgen_make_run(
537     CG_SUBCOMMAND       cmd
538 )
539 {
540     do_user_action(CG_GOAL_MAKE_AND_RUN, cmd);
541 }
542
543
544 /*************************************************************************
545  *************************************************************************
546  **                                                                     **
547  **      PRIVATE ENTRY POINTS                                           **
548  **                                                                     **
549  *************************************************************************
550  *************************************************************************/
551
552
553 /*************************************************************************
554 **                                                                      **
555 **      Handle the terminal emulator                                    **
556 **                                                                      **
557 *************************************************************************/
558
559 /*
560  * Prints the string to the terminal. Always flushes.
561  */
562 static int
563 print_to_term(STRING msg)
564 {
565     int                 rc = 0;
566     FILE               *fp = NULL;
567
568     if ((rc = get_slave_device_name()) < 0)
569     {
570         return rc;
571     }
572     fp = util_fopen_locked(istr_string(termSlaveDeviceName), "w");
573     if (fp == NULL)
574     {
575         return -1;
576     }
577     fprintf(fp, "%s", msg);
578     util_fclose(fp);
579     return 0;
580 }
581
582 /*
583  * Sends stdout and stderr to term window
584  */
585 static int
586 send_output_to_term()
587 {
588     static BOOL         done = FALSE;
589     int                 rc = 0;
590
591     if (done)
592     {
593         return 0;
594     }
595     if ((rc = get_slave_device_name()) < 0)
596     {
597         return rc;
598     }
599
600     done = TRUE;
601     freopen(istr_string(termSlaveDeviceName), "w", stdout);
602     freopen(istr_string(termSlaveDeviceName), "w", stderr);
603
604     return 0;
605 }
606
607
608 static int
609 term_execute_command(CG_SUBCOMMAND cmd_code, STRING cmd, STRING argv[])
610 {
611     CG_STATUS           status_code = CG_STATUS_UNDEF;
612     int                 exit_code = 0;
613     int                 rc = 0;
614     pid_t               rc_pid = INVALID_PID;
615     pid_t               watchdog_pid = INVALID_PID;
616     int                 child_status = 0;
617     STRING              msg = NULL;
618     int                 i= 0, msg_size = 0;
619
620     if (actual_process_pgid != INVALID_PID)
621     {
622         return -1;
623     }
624     if ((rc = get_slave_device_name()) < 0)
625     {
626         return rc;
627     }
628     if (cmd == NULL)
629     {
630         return 0;
631     }
632
633     msg_size = strlen("====> Running: ");
634     for (i = 0; argv[i] != NULL; ++i)
635     {
636         msg_size += strlen(argv[i]) + 1;
637     }    
638     msg = (STRING) XtMalloc(msg_size + 1);  /* Add 1 for NULL */
639     strcpy(msg, "====> Running: ");
640     strcat(msg, cmd);
641     for (i= 1; argv[i] != NULL; ++i)
642     {
643         strcat(msg, " ");
644         strcat(msg, argv[i]);
645     }
646     strcat(msg, "\n");
647     print_to_term(msg);
648
649     /*
650      * Clean up (possible) zombie watchdog process(es)
651      */
652     wait_for_child();
653
654     create_status_pipe();
655     watchdog_pid = fork();
656     if (watchdog_pid == (pid_t)-1)
657     {
658         /* error occured! */
659         util_printf_err("Could not create subprocess: %s\n",
660                 strerror(errno));
661         watchdog_pid = INVALID_PID;
662         write_to_status_pipe(CG_STATUS_ERROR, cmd_code, (void*)0);
663     }
664     else if (watchdog_pid == 0)
665     {
666         /* child - "watchdog" process */
667         int             rc = 0;
668         pid_t           actual_process_pid = INVALID_PID;
669
670         /*
671          * For some reason, if the SIGCHLD signal is not blocked,
672          * waitpid() returns -1, with errno == EINTR. This is exactly
673          * the opposite of the behavior I would expect, but it seems
674          * to work fine.
675          */
676         {
677             sigset_t    signals;
678             sigemptyset(&signals);
679             sigaddset(&signals, SIGCHLD);
680             sigprocmask(SIG_BLOCK, &signals, NULL);
681         }
682
683         util_fdclose(status_pipe_read);
684
685         send_output_to_term();
686         actual_process_pid = fork();
687         if (actual_process_pid == INVALID_PID)
688         {
689             /* error! */
690             fprintf(stderr, "Could not create subprocess: %s\n",
691                 strerror(errno));
692             write_to_status_pipe(CG_STATUS_ERROR, cmd_code, (void*)0);
693             subprocess_exit(1);
694         }
695         else if (actual_process_pid == 0)
696         {
697             /* grandchild - becomes the program we are actually running */
698             int         num_env_vars = strlist_get_num_strs(user_env_vars);
699             int i = 0;
700             STRING      var_name = NULL;
701             STRING      var_value = NULL;
702             STRING      putenv_var = NULL;
703             int         putenv_var_size = 0;
704
705             setpgid(0,0);               /* make process group leader */
706             write_to_status_pipe(CG_STATUS_STARTED, cmd_code, (void*)getpgrp());
707
708             for (i = 0; i < num_env_vars; ++i)
709             {
710                 var_value = NULL;
711                 var_name = strlist_get_str(user_env_vars,i, (void **)&var_value);
712                 if (!util_strempty(var_value))
713                 {
714                     putenv_var_size = strlen(var_name) + strlen(var_value) + 2;
715                                                 /* +2 for "=" and NULL */
716                     putenv_var = (STRING)util_malloc(putenv_var_size);
717                     sprintf(putenv_var, "%s=%s", var_name, var_value);
718                 }
719                 else
720                 {
721                     putenv_var_size = strlen(var_name) + 2;
722                     putenv_var = (STRING)util_malloc(putenv_var_size);
723                     sprintf(putenv_var, "%s=", var_name);
724                 }
725                 util_putenv(putenv_var);
726             }
727
728             /*
729              * Close status pipe and start command!
730              */
731             util_fdclose(status_pipe_read);
732             util_fdclose(status_pipe_write);
733             if (execvp(cmd, argv) == -1)
734             {
735                 perror(cmd);
736             }
737             subprocess_exit(1);
738         } /* grandchild (actual work process) */
739         else
740         {
741             /* child ("watchdog" process) */ 
742             int         grandchild_status = 0;
743             pid_t       rc_pid = INVALID_PID;   /* returned pid */
744
745             /*util_dprintf(3, "process launched: %ld\n", (long)actual_process_pid);*/
746
747             rc_pid = waitpid(actual_process_pid, &grandchild_status, 0);
748             /*printf("frontline: child done!\n");*/
749             if (rc_pid == INVALID_PID)
750             {
751                 /* damn! an error occured... */
752                 /*printf("frontline: error waiting for child! (%s)\n",
753                         util_strsafe(strerror(errno)));*/
754                 status_code = CG_STATUS_ERROR;
755                 exit_code= -1;
756             }
757             else
758             {
759                 exit_code= -1;
760                 if (WIFEXITED(grandchild_status))
761                 {
762                     /* grandchild exited */
763                     /* printf("frontline: grandchild exited(%d)\n", 
764                         WEXITSTATUS(grandchild_status));*/
765                     status_code = CG_STATUS_EXITED;
766                     exit_code = WEXITSTATUS(grandchild_status);
767                 }
768                 else if (WIFSIGNALED(grandchild_status))
769                 {
770                     /* child was killed by uncaught signal */
771                     int         kill_signal = WTERMSIG(grandchild_status);
772                     STRING      signalName = NULL;
773                     time_t      signalTime = time(NULL);
774                     BOOL        coreDumped = FALSE;
775                     struct stat fileInfo;
776                     /*printf("frontline: child signalled(%d)\n", kill_signal);*/
777
778                     status_code = CG_STATUS_SIGNALLED;
779                     exit_code = kill_signal;
780
781                     if (stat("core", &fileInfo) == 0)
782                     {
783                         if (labs(difftime(signalTime, fileInfo.st_mtime)) < 5)
784                         {
785                             coreDumped = TRUE;
786                         }
787                     }
788
789                     switch (kill_signal)
790                     {
791                         case SIGABRT: signalName = "Aborted";
792                         break;
793                         case SIGALRM: signalName = "Uncaught alarm";
794                         break;
795                         #ifdef SIGBUS   /* SIGBUS is not POSIX */
796                         case SIGBUS: signalName = "Bus error";
797                         break;
798                         #endif
799                         case SIGFPE: signalName = "Arithmetic exception";
800                         break;
801                         case SIGHUP: signalName = "Hangup";
802                         break;
803                         case SIGILL: signalName = "Illegal instruction";
804                         break;
805                         case SIGINT: signalName = "Interrupted";
806                         break;
807                         case SIGKILL: signalName = "Killed";
808                         break;
809                         case SIGPIPE: signalName = "Write to bad pipe";
810                         break;
811                         case SIGQUIT: signalName = "Quit";
812                         break;
813                         case SIGSEGV: signalName = "Segmentation fault";
814                         break;
815                         case SIGTERM: signalName = "Terminated";
816                         break;
817                         default:
818                         {
819                             static char sigmsg[30];
820                             sprintf(sigmsg, "Uncaught signal %d", kill_signal);
821                         }
822                         break;
823                     }
824
825                     fprintf(stderr, "====> %s", signalName);
826                     if (coreDumped)
827                     {
828                         fprintf(stderr, " (Core dumped)");
829                     }
830                     fprintf(stderr, "\n");
831                 }
832             }
833
834             if (status_pipe_write >= 0)
835             {
836                 write_to_status_pipe(status_code, cmd_code, (void*)exit_code);
837                 util_fdclose(status_pipe_write);
838             }
839             subprocess_exit(exit_code);
840         } /* child (watchdog) */
841
842         /* This block should never execute */
843         assert(("Bad block executed",TRUE));
844         subprocess_exit(1);
845     }
846     else
847     {
848         /* parent */
849         add_child_to_list(watchdog_pid);
850         util_fdclose(status_pipe_write);
851     }
852
853     return 0;
854 }
855
856
857 /*
858  * Gets the slave device name and puts it into the file variable
859  */
860 static int
861 get_slave_device_name()
862 {
863     String              deviceName = NULL;
864
865     if (termSlaveDeviceName != NULL)
866     {
867         return 0;
868     }
869
870     if (termSlaveDeviceName == NULL)
871     {
872         XtVaGetValues(output_termWidget, DtNtermSlaveName, &deviceName, NULL);
873         if (deviceName != NULL)
874         {
875             termSlaveDeviceName= istr_create(deviceName);
876             /*util_dprintf(2,"slavename: '%s'\n", util_strsafe(deviceName));*/
877         }
878     }   /* deviceName == NULL */
879
880     return (termSlaveDeviceName == NULL ? -1 : 0);
881 }
882
883
884 /*************************************************************************
885 **                                                                      **
886 **      Implement the functions                                         **
887 **                                                                      **
888 *************************************************************************/
889
890 static int
891 exec_generate_code()
892 {
893     ABObj       project= proj_get_project();
894     STRING      project_name= NULL;
895
896     if (project == NULL)
897     {
898         return -1;
899     }
900     project_name = obj_get_name(project);
901     if (project_name == NULL)
902     {
903         return -1;
904     }
905
906     switch (CodeGenOptions.cmd_flag)
907     {
908         case CG_GEN_SPECIFIC_FILES_FLAG:
909             exec_generate_specific_files();
910         break;
911  
912         case CG_GEN_SPECIFIC_FILES_AND_MAIN_FLAG:
913             exec_generate_specific_files_and_main();
914         break;
915  
916         case CG_GEN_MAIN_FLAG:
917             exec_generate_main();
918         break;
919
920         case CG_GEN_PROJ_FLAG:
921             exec_generate_proj();
922         break;
923  
924         default:
925         break;
926     }
927     return 0;
928 }
929
930 static int
931 exec_generate_main()
932 {
933     int         rc = 0;         /* return code */
934     ABObj       project= proj_get_project();
935     STRING      project_name= NULL;
936     STRING      argv[MAX_CGEN_FIXED_ARGS];
937     int         i = 0;
938     STRING      cmd = NULL;
939
940     if (project == NULL)
941     {
942         return -1;
943     }
944     project_name = obj_get_name(project);
945     if (project_name == NULL)
946     {
947         return -1;
948     }
949
950     for (i = 0; i < MAX_CGEN_FIXED_ARGS; i++)
951         argv[i] = NULL;
952
953     i = 0;
954     if ((rc = build_dtcodegen_arg_list(argv, &i)) < 0)
955     {
956         return rc;
957     }
958
959     argv[i]= "-main";           i++;
960     argv[i]= "-p";              i++;
961     argv[i]= project_name;      i++;
962     argv[i]= NULL;
963
964     term_execute_command(CG_CMD_GEN_CODE, argv[0], argv);
965     return 0;
966 }
967  
968 static int
969 exec_generate_proj()
970 {
971     int         rc = 0;         /* return code */
972     ABObj       project= proj_get_project();
973     STRING      project_name= NULL;
974     STRING      argv[MAX_CGEN_FIXED_ARGS];
975     int         i = 0;
976     STRING      cmd = NULL;
977  
978     if (project == NULL)
979     {
980         return -1;
981     }
982     project_name= obj_get_name(project);
983     if (project_name == NULL)
984     {
985         return -1;
986     }
987  
988     for (i = 0; i < MAX_CGEN_FIXED_ARGS; i++) 
989         argv[i] = NULL;
990
991     i = 0;
992     if ((rc = build_dtcodegen_arg_list(argv, &i)) < 0)
993     {
994         return rc;
995     }
996        
997     argv[i]= "-p";              i++;
998     argv[i]= project_name;      i++;
999     argv[i]= NULL;
1000     term_execute_command(CG_CMD_GEN_CODE, argv[0], argv);
1001  
1002     return 0;
1003 }
1004  
1005 static int
1006 exec_generate_specific_files()
1007 {
1008     int         returnValue = 0;
1009     int         rc = 0;         /* return code */
1010     ABObj       project= proj_get_project();
1011     STRING      project_name= NULL;
1012     STRING      *argv;
1013     int         argv_size = MAX_CGEN_FIXED_ARGS;
1014     int         i, n, num_args, arg_count, num_mods;
1015  
1016     if (project == NULL)
1017     {
1018         return -1;
1019     }
1020     project_name= obj_get_name(project);
1021     if (project_name == NULL)
1022     {
1023         return -1;
1024     }
1025
1026     num_mods = strlist_get_num_strs(CodeGenOptions.module_list);
1027     argv_size += num_mods;
1028     argv = (STRING *)util_malloc(argv_size * sizeof(STRING));
1029     for (i = 0; i < argv_size; i++)
1030         argv[i] = NULL;
1031  
1032     arg_count = 0;
1033     if ((rc = build_dtcodegen_arg_list(argv, &arg_count)) < 0)
1034     {
1035         returnValue = rc;
1036         goto epilogue;
1037     }
1038
1039     argv[arg_count]= "-p";              arg_count++;
1040     argv[arg_count]= project_name;      arg_count++;
1041  
1042     num_args = arg_count + num_mods;
1043     for (i = arg_count, n = 0; i < num_args; i++, n++)
1044     {
1045         argv[i] = strlist_get_str(CodeGenOptions.module_list, n, (void **)NULL);
1046     }
1047     argv[num_args] = NULL;
1048      
1049     term_execute_command(CG_CMD_GEN_CODE, argv[0], argv);
1050
1051 epilogue:
1052     util_free(argv);
1053     return returnValue;
1054 }
1055  
1056 static int
1057 exec_generate_specific_files_and_main()
1058 {
1059     int         returnValue = 0;
1060     int         rc = 0;         /* return code */
1061     ABObj       project= proj_get_project();
1062     STRING      project_name= NULL;
1063     STRING      *argv;
1064     int         argv_size = MAX_CGEN_FIXED_ARGS;
1065     int         i, n, num_args, arg_count, num_mods;
1066  
1067     if (project == NULL)
1068     {
1069         return -1;
1070     }
1071     project_name= obj_get_name(project);
1072     if (project_name == NULL)
1073     {
1074         return -1;
1075     }
1076
1077     num_mods = strlist_get_num_strs(CodeGenOptions.module_list);
1078     argv_size += num_mods;
1079     argv = (STRING *)util_malloc(argv_size * sizeof(STRING));
1080     for (i = 0; i < argv_size; i++) 
1081         argv[i] = NULL;
1082
1083     arg_count = 0;
1084     if ((rc = build_dtcodegen_arg_list(argv, &arg_count)) < 0)
1085     {
1086         returnValue = rc;
1087         goto epilogue;
1088     }
1089
1090     argv[arg_count]= "-main";           arg_count++;
1091     argv[arg_count]= "-p";              arg_count++;
1092     argv[arg_count]= project_name;      arg_count++;
1093  
1094     num_args = arg_count + num_mods;
1095     for (i = arg_count, n = 0; i < num_args; i++, n++)
1096     {
1097         argv[i] = strlist_get_str(CodeGenOptions.module_list, n, (void **)NULL);
1098     }
1099     argv[num_args] = NULL;
1100      
1101     term_execute_command(CG_CMD_GEN_CODE, argv[0], argv);
1102
1103 epilogue:
1104     util_free(argv);
1105     return returnValue;
1106 }
1107
1108
1109 /*
1110  * Builds the code generator options that are common to all the code
1111  * generation goals.
1112  *
1113  * argList[0] = the executable
1114  *
1115  * The individual strings returned in argList are pointers to static
1116  * storage and should not be freed by the caller.
1117  *
1118  * Returns the number of arguments that were added.
1119  *
1120  * ASSUMES: the argList array is large enough to handle all args, plus a NULL
1121  */
1122 static int
1123 build_dtcodegen_arg_list(STRING *argList, int *iInOut)
1124 {
1125     int         returnValue = 0;
1126     int         rc = 0;
1127     int         i = (*iInOut);
1128     int         numArgsAdded = 0;
1129     STRING      dtcodegenCmd = NULL;
1130
1131     if ((rc = select_command(dtcodegenCmdList, &dtcodegenCmd)) < 0)
1132     {
1133         print_cmd_not_found_message("dtcodegen");
1134         returnValue = rc;
1135         goto epilogue;
1136     }
1137
1138     argList[i++]= dtcodegenCmd;
1139     argList[i++]= "-changed";           /* everybody gets this one */
1140
1141     if (CodeGenOptions.no_merge)     
1142     {
1143         argList[i++] = "-nomerge";
1144         ++numArgsAdded;
1145     }
1146  
1147     switch (CodeGenOptions.verbosity)
1148     {
1149         case CG_VERBOSITY_SILENT:
1150             argList[i++] = "-s";
1151             ++numArgsAdded;
1152         break;
1153          
1154         case CG_VERBOSITY_VERBOSE:
1155             argList[i++] = "-v";
1156             ++numArgsAdded;
1157         break;
1158     }
1159
1160     argList[i] = NULL;
1161
1162 epilogue:
1163     if (returnValue >= 0)
1164     {
1165         /* successful - return i */
1166         returnValue = numArgsAdded;
1167         (*iInOut) = i;
1168     }
1169     return returnValue;
1170 }
1171
1172
1173 static int
1174 exec_make()
1175 {
1176     int                 rc = 0;         /* return code */
1177     STRING              cmd = NULL;
1178     STRING              argv[5];
1179     BOOL                continueMake = FALSE;
1180
1181     rc= check_makefile(&continueMake);
1182     if ((rc < 0) || (!continueMake))
1183     {
1184         return rc;
1185     }
1186
1187     if ((rc = select_command(makeCmdList, &cmd)) < 0)
1188     {
1189         print_cmd_not_found_message(makeCmdList[0]);
1190         return rc;
1191     }
1192
1193     argv[0] = cmd;
1194     argv[1] = CodeGenOptions.make_args;
1195     argv[2] = NULL;
1196     term_execute_command(CG_CMD_MAKE, argv[0], argv);
1197     return 0;
1198 }
1199
1200
1201 static int
1202 exec_run()
1203 {
1204     ABObj       project= NULL;
1205     char        executable_name[1024];
1206     char        cmd[1024];
1207     STRING      argv[5];
1208     *executable_name = 0;
1209     *cmd = 0;
1210
1211     project= proj_get_project();
1212     if ((project == NULL) || (obj_get_name(project) == NULL))
1213     {
1214         return -1;
1215     }
1216
1217     cvt_type_to_ident(obj_get_name(project), executable_name, 1024);
1218     sprintf(cmd, "./%s", executable_name);
1219     argv[0] = cmd;
1220     argv[1] = CodeGenOptions.run_args;
1221     argv[2] = NULL;
1222     term_execute_command(CG_CMD_RUN, argv[0], argv);
1223
1224     return 0;
1225 }
1226
1227
1228 /*
1229  * cmd_code is the code of the command that just finished.  Executes
1230  * the next command necessary to achieve the user's goal.
1231  *
1232  * If cmd_code is CG_CMD_UNDEF, assumes that no commands have been
1233  * issued, and issues the first command to achieve the goal.
1234  */
1235 static int
1236 exec_next_command(CG_SUBCOMMAND cmd_code, int exit_code)
1237 {
1238     int return_value= 0;
1239
1240     if (cmd_code == CG_CMD_UNDEF)
1241     {
1242         print_to_term("\n\n");
1243         goto_busy_state();
1244     }
1245
1246     switch (user_goal)
1247     {
1248         case CG_GOAL_GEN_CODE:
1249             return_value= exec_next_command_for_gen_code(cmd_code, exit_code);
1250         break;
1251
1252         case CG_GOAL_MAKE:
1253             return_value= exec_next_command_for_make(cmd_code, exit_code);
1254         break;
1255
1256         case CG_GOAL_RUN:
1257             return_value= exec_next_command_for_run(cmd_code, exit_code);
1258         break;
1259
1260         case CG_GOAL_MAKE_AND_RUN:
1261             return_value = exec_next_command_for_build_and_run(cmd_code, exit_code);
1262         break;
1263
1264         default:
1265             goto_ready_state();
1266             return_value= ERR_INTERNAL;
1267         break;
1268     }
1269
1270     if (return_value < 0)
1271     {
1272         goto_ready_state();
1273     }
1274
1275     return return_value;
1276 }
1277
1278 static int
1279 exec_next_command_for_gen_code(CG_SUBCOMMAND cmd_code, int exit_code)
1280 {
1281     int         rc= 0;  /* return code */
1282     exit_code = exit_code;      /* avoid warning */
1283
1284     switch (cmd_code)
1285     {
1286         case CG_CMD_UNDEF:
1287             rc= exec_generate_code();
1288         break;
1289
1290         case CG_CMD_GEN_CODE:
1291             print_success_message();
1292         break;
1293
1294         default:
1295         break;
1296     }
1297     return 0;
1298 }
1299
1300
1301 static int
1302 exec_next_command_for_make(CG_SUBCOMMAND cmd_code, int exit_code)
1303 {
1304     exit_code = exit_code;      /* avoid warning */
1305
1306     switch (cmd_code)
1307     {
1308         case CG_CMD_UNDEF:
1309         case CG_CMD_GEN_CODE:   /* was run to get Makefile */
1310             exec_make();
1311         break;
1312
1313         case CG_CMD_MAKE:
1314             print_success_message();
1315         break;
1316
1317         default:
1318         break;
1319     }
1320
1321     return 0;
1322 }
1323
1324 static int
1325 exec_next_command_for_run(CG_SUBCOMMAND cmd_code, int exit_code)
1326 {
1327     switch (cmd_code)
1328     {
1329         case CG_CMD_UNDEF:
1330             exec_run();
1331         break;
1332
1333         case CG_CMD_RUN:
1334             print_exit_message(exit_code);
1335         break;
1336
1337         default:
1338         break;
1339     }
1340
1341     return 0;
1342 }
1343
1344 static int
1345 exec_next_command_for_build_and_run(CG_SUBCOMMAND cmd_code, int exit_code)
1346 {
1347     switch (cmd_code)
1348     {
1349         case CG_CMD_UNDEF:
1350             /* this may do a generate and/or a make */
1351             exec_first_build_and_run_command();
1352         break;
1353
1354         case CG_CMD_GEN_CODE:
1355             exec_make();
1356         break;
1357
1358         case CG_CMD_MAKE:
1359             exec_run();
1360         break;
1361
1362         case CG_CMD_RUN:
1363             print_exit_message(exit_code);
1364         break;
1365
1366         default:
1367         break;
1368     } /* cmd_code */
1369
1370     return 0;
1371 }
1372
1373
1374 static int
1375 exec_first_build_and_run_command()
1376 {
1377     int return_value= 0;
1378     if (!util_file_exists("Makefile"))
1379     {
1380         return_value= exec_generate_proj();
1381     }
1382     else
1383     {
1384         return_value= exec_make();
1385     }
1386     return return_value;
1387 }
1388
1389
1390 static int
1391 wait_for_child(void)
1392 {
1393     pid_t       childPid = INVALID_PID;
1394     pid_t       rcPid = INVALID_PID;
1395     int         i = 0;
1396     int         numExited = 0;
1397
1398     for (i = 0; i < XtNumber(child_pid_list); ++i)
1399     {
1400         if ((childPid = child_pid_list[i]) == INVALID_PID)
1401         {
1402             continue;
1403         }
1404         if (   ((rcPid = waitpid(childPid, (int*)0, WNOHANG)) == childPid)
1405             || (kill(childPid,0) == -1))
1406         {
1407             child_pid_list[i] = INVALID_PID;
1408             ++numExited;
1409         }
1410     }
1411
1412     return numExited;
1413 }
1414
1415
1416 static int
1417 add_child_to_list(pid_t childPid)
1418 {
1419     int         i = 0;
1420     BOOL        added = FALSE;
1421     int         attemptCount = 0;
1422     int         maxAttempts = EXIT_SLEEP_SECONDS+2;
1423
1424     for (attemptCount = 0; (!added) && (attemptCount < maxAttempts);
1425         ++attemptCount)
1426     {
1427         if (attemptCount > 0)
1428         {
1429             sleep(1);           /* wait for child to exit, making room */
1430         }
1431         wait_for_child();       /* make some room */
1432
1433         for (i = 0; i < XtNumber(child_pid_list); ++i)
1434         {
1435             if (child_pid_list[i] == INVALID_PID)
1436             {
1437                 child_pid_list[i] = childPid;
1438                 added = TRUE;
1439                 break;
1440             }
1441         }
1442     }
1443
1444     return added?0:-1;
1445 }
1446
1447 static int
1448 subprocess_exit(int exit_code)
1449 {
1450     if (status_pipe_write >= 0)
1451     {
1452         util_fdsync(status_pipe_write);
1453     }
1454     util_fdclose(status_pipe_write);
1455     util_fdclose(status_pipe_read);
1456
1457     /*
1458      * On AIX, writing to a pipe and immediately exiting seems to guarantee
1459      * that not all the data will reach the receiving end of the pipe.
1460      * closing the pipe and sleeping for a few seconds seems to work well.
1461      */
1462     if (util_get_os_type() == AB_OS_AIX)
1463     {
1464         sleep(EXIT_SLEEP_SECONDS);
1465     }
1466     _exit(exit_code);
1467     return -1;
1468 }
1469
1470
1471 static int
1472 print_internal_err_message()
1473 {
1474     print_to_term("****> UNSUCCESSFUL (Internal failure occurred).\n");
1475     return 0;
1476 }
1477
1478
1479 static int
1480 print_failure_message(CG_SUBCOMMAND cmd_code, int exit_code)
1481 {
1482     char        msg[256];
1483     cmd_code= cmd_code;
1484     sprintf(msg, "****> UNSUCCESSFUL (Command exited with code %d).\n",
1485                 exit_code);
1486     print_to_term(msg);
1487     user_goal= CG_GOAL_UNDEF;
1488     goto_ready_state();
1489     return 0;
1490 }
1491
1492
1493 static int
1494 print_success_message()
1495 {
1496     print_to_term("====> Completed successfully.\n");
1497     user_goal= CG_GOAL_UNDEF;
1498     goto_ready_state();
1499     return 0;
1500 }
1501
1502
1503 static int
1504 print_cmd_not_found_message(STRING cmd)
1505 {
1506     char        msg[1024];
1507     *msg = 0;
1508
1509     sprintf(msg, 
1510     "****> ERROR - Could not find command '%s'.\n"
1511     "****>         Please check your PATH variable (This can be\n"
1512     "****>         done via the Options->Environment menu).\n",
1513         cmd);
1514     print_to_term(msg);
1515
1516     user_goal= CG_GOAL_UNDEF;
1517     goto_ready_state();
1518     return 0;
1519 }
1520
1521
1522 static int
1523 print_death_message(void)
1524 {
1525     char msg[256];
1526     sprintf(msg, 
1527 "****> Program died a horrible, unnatural death, due to an uncaught signal\n");
1528     print_to_term(msg);
1529     user_goal = CG_GOAL_UNDEF;
1530     return goto_ready_state();
1531 }
1532
1533
1534 static int
1535 print_exit_message(int exitCode)
1536 {
1537     char msg[256];
1538     sprintf(msg, "====> Program exited (exit code %d)\n", exitCode);
1539     print_to_term(msg);
1540     user_goal= CG_GOAL_UNDEF;
1541     goto_ready_state();
1542     return 0;
1543 }
1544
1545
1546 static int
1547 print_abort_message()
1548 {
1549     print_to_term("\n====> Command aborted.\n");
1550     user_goal= CG_GOAL_UNDEF;
1551     goto_ready_state();
1552     return 0;
1553 }
1554
1555
1556 static void
1557 pipe_data_ready_proc(
1558     XtPointer   client_data,
1559     int         *fid,
1560     XtInputId   *id
1561 )
1562 {
1563     BOOL                aborted = FALSE;
1564     CG_SUBCOMMAND       cmd_code = CG_CMD_UNDEF;
1565     CG_STATUS           status_code = CG_STATUS_UNDEF;
1566     void                *status_data = NULL;
1567     int                 int_status_code = 0;
1568     int                 int_cmd_code = 0;
1569     int                 exit_code = 0;
1570     int                 kill_signal = 0;
1571     pid_t               rc_pid = INVALID_PID;
1572     id = id;    /* avoid warning */
1573
1574     /*util_dprintf(3, "rcv - data ready on pipe...\n");*/
1575     if (read_from_status_pipe(&status_code, &cmd_code, &status_data) < 0)
1576     {
1577         status_code = CG_STATUS_ERROR;
1578         goto epilogue;
1579     }
1580     aborted = (   (abortingPID != INVALID_PID)
1581                && (abortingPID == actual_process_pgid) );
1582
1583     switch (status_code)
1584     {
1585         case CG_STATUS_STARTED:
1586             actual_process_pgid = (pid_t)status_data;
1587             /*util_dprintf(2,"rcv started: %ld\n", (long)actual_process_pgid);*/
1588         break;
1589
1590         case CG_STATUS_EXITED:
1591             exit_code = (int)status_data;
1592             actual_process_pgid = INVALID_PID;
1593             /*util_dprintf(2,"rcv exit(%d)\n", exit_code);*/
1594             if (aborted)
1595             {
1596                 /* message gets printed below */
1597             }
1598             else if ((exit_code != 0) && (cmd_code != CG_CMD_RUN))
1599             {
1600                 print_failure_message(cmd_code, exit_code);
1601                 goto_ready_state();
1602             }
1603             else
1604             {
1605                 exec_next_command(cmd_code, exit_code);
1606             }
1607         break;
1608
1609         case CG_STATUS_SIGNALLED:
1610             kill_signal = (int)status_data;
1611             /*util_dprintf(2,"rcv signalled(%d)\n", kill_signal);*/
1612             actual_process_pgid = INVALID_PID;
1613             goto_ready_state();
1614         break;
1615
1616         case CG_STATUS_ERROR:
1617             print_internal_err_message();
1618             if (actual_process_pgid != INVALID_PID)
1619             {
1620                 if (careful_kill_group(actual_process_pgid) >= 0)
1621                 {
1622                     actual_process_pgid = INVALID_PID;
1623                 }
1624             }
1625             goto_ready_state();
1626         break;
1627
1628         default:
1629             if (actual_process_pgid != INVALID_PID)
1630             {
1631                 if (careful_kill_group(actual_process_pgid) >= 0)
1632                 {
1633                     actual_process_pgid = INVALID_PID;
1634                 }
1635                 goto_ready_state();
1636             }
1637         break;
1638     }
1639
1640     if (aborted)
1641     {
1642         print_abort_message();
1643         goto_ready_state();
1644     }
1645
1646     if (actual_process_pgid == INVALID_PID)
1647     {
1648         abortingPID = INVALID_PID;
1649     }
1650
1651 epilogue:
1652     wait_for_child();   /* clean up (possible) zombie watchdog processes */
1653     return;
1654 }
1655
1656
1657 /*
1658  * We are running a process - desensitize most buttons.
1659  */
1660 static int
1661 goto_busy_state()
1662 {
1663     XtSetSensitive(abort_button, True);
1664     XtSetSensitive(abort_item, True);
1665
1666     XtSetSensitive(cgen_props_item, False);
1667     XtSetSensitive(gen_code_button, False);
1668     XtSetSensitive(gen_code_item, False);
1669     XtSetSensitive(make_button, False);
1670     XtSetSensitive(make_item, False);
1671     XtSetSensitive(make_run_button, False);
1672     XtSetSensitive(make_run_item, False);
1673     XtSetSensitive(run_button, False);
1674     XtSetSensitive(run_item, False);
1675     return 0;
1676 }
1677
1678
1679 /*
1680  * We are waiting for user input
1681  */
1682 static int
1683 goto_ready_state()
1684 {
1685     XtSetSensitive(abort_button, False);
1686     XtSetSensitive(abort_item, False);
1687
1688     XtSetSensitive(cgen_props_item, True);
1689     XtSetSensitive(gen_code_button, True);
1690     XtSetSensitive(gen_code_item, True);
1691     XtSetSensitive(make_button, True);
1692     XtSetSensitive(make_item, True);
1693     XtSetSensitive(make_run_button, True);
1694     XtSetSensitive(make_run_item, True);
1695     XtSetSensitive(run_button, True);
1696     XtSetSensitive(run_item, True);
1697
1698     return 0;
1699 }
1700
1701
1702 static int
1703 create_status_pipe(void)
1704 {
1705     BOOL        pipeOpen = TRUE;
1706     /*util_dprintf(2,"create_status_pipe()\n");*/
1707
1708     if ((status_pipe_read < 0) || (status_pipe_write < 0))
1709     {
1710         int                 fds[2];
1711
1712         destroy_status_pipe();
1713         if (pipe(fds) == 0)
1714         {
1715             pipeOpen = TRUE;
1716             status_pipe_read = fds[0]; fds[0] = -1;
1717             status_pipe_write = fds[1]; fds[1] = -1;
1718         }
1719
1720         if (   (input_proc_id == -1) 
1721             && (status_pipe_read >= 0)
1722             && (output_termWidget != NULL))
1723         {
1724             XtAppContext        app_context= XtWidgetToApplicationContext(output_termWidget);
1725             input_proc_id = 
1726                 XtAppAddInput(
1727                     app_context, 
1728                     status_pipe_read,
1729                     (XtPointer)XtInputReadMask,
1730                     pipe_data_ready_proc,
1731                     NULL
1732                     );
1733         }
1734     }
1735
1736
1737 #ifdef DEBUG
1738     if (!pipeOpen)
1739     {
1740         util_dprintf(1, "CGEN WINDOW: COULD NOT CREATE STATUS PIPE\n");
1741     }
1742 #endif /* DEBUG */
1743     return pipeOpen?0:-1;
1744 }
1745
1746
1747 static int
1748 destroy_status_pipe(void)
1749 {
1750     /*util_dprintf(2,"destroy_status_pipe()\n");*/
1751     util_fdclose(status_pipe_read);
1752     util_fdclose(status_pipe_write);
1753
1754     if (input_proc_id != -1)
1755     {
1756         XtRemoveInput(input_proc_id); input_proc_id = -1;
1757     }
1758
1759     return 0;
1760 }
1761
1762
1763 static int
1764 write_to_status_pipe(
1765                         CG_STATUS       status_code, 
1766                         CG_SUBCOMMAND   cmd_code, 
1767                         void            *status_data
1768 )
1769 {
1770     int         int_status_code = (int)status_code;
1771     int         int_cmd_code= (int)cmd_code;
1772
1773     /*printf("write to pipe: %d %d %d\n", 
1774                 (int)status_code, (int)cmd_code, (int)status_data);*/
1775     write(status_pipe_write, (void*)&int_status_code, sizeof(int));
1776     write(status_pipe_write, (void*)&int_cmd_code, sizeof(int));
1777     write(status_pipe_write, (void*)&status_data, sizeof(void*));
1778
1779     /*
1780      * The parent always keeps the write file descriptor open, and
1781      * on HP and IBM, each write may not get flushed. Force it.
1782      */
1783     util_fdsync(status_pipe_write);
1784
1785     return 0;
1786 }
1787
1788 static int      
1789 read_from_status_pipe(
1790                         CG_STATUS       *status_code_out,
1791                         CG_SUBCOMMAND   *cmd_code_out,
1792                         void            **status_data_out
1793 )
1794 {
1795     int         return_value = 0;
1796     int         int_status_code = 0;
1797     int         int_cmd_code = 0;
1798     void        *status_data = NULL;
1799     int         nread = 0;
1800
1801     /* util_dprintf(2,"Data ready\n");*/
1802     if (status_pipe_write >= 0)
1803     {
1804         util_fdsync(status_pipe_write);
1805     }
1806     nread += read(status_pipe_read, (void *)&int_status_code, sizeof(int));
1807     nread += read(status_pipe_read, (void *)&int_cmd_code, sizeof(int));
1808     nread += read(status_pipe_read, (void *)&status_data, sizeof(void*));
1809     (*status_code_out) = (CG_STATUS)int_status_code;
1810     (*cmd_code_out) = (CG_SUBCOMMAND)int_cmd_code;
1811     (*status_data_out) = status_data;
1812
1813     return_value = nread;
1814     if (nread < 1)
1815     {
1816         /* The write end of the pipe is apparently closed */
1817         destroy_status_pipe();
1818         return_value = -1;
1819     }
1820
1821     /*printf("rcv (fd:%d bytes:%d) read from pipe: %d %d %d\n", 
1822         status_pipe_read, nread,
1823         (int)(*status_code_out), 
1824         (int)(*cmd_code_out), 
1825         (int)(*status_data_out));*/
1826
1827     return return_value;
1828 }
1829
1830
1831 /*
1832  * Tries to kill the process in a "friendly" way.  Sends SIGTERM first,
1833  * waits 5 seconds, and then sends SIGKILL.
1834  *
1835  * Returns >= 0 if successfully killed, <0 otherwise
1836  */
1837 static int
1838 careful_kill_group(pid_t pgid)
1839 {
1840     Bool        killed = False;
1841     int         rc = 0;
1842     pid_t       leader_pid = pgid;
1843     int         child_status = 0;
1844     /* pid_t    pid_done = INVALID_PID; */
1845     int         waitcount = 0;
1846     long        kill_pgrp_id = (long)(-1 * pgid); /* negative pid = group id */
1847
1848     /*util_dprintf(2, "careful_kill_group(%ld)\n", (long)pgid);*/
1849
1850     /*
1851      * Try SIGTERM
1852      */
1853     kill(kill_pgrp_id, SIGTERM);
1854     for (waitcount= 0; (!killed) && (waitcount < 5); ++waitcount)
1855     {
1856         if (kill(kill_pgrp_id, 0) == -1)        /* sig 0 checks pid only */
1857         {
1858             /* kill failed, so group doesn't exist, any more */
1859             killed = TRUE;
1860         }
1861         if (!killed)
1862         {
1863             sleep(1);
1864         }
1865     }
1866
1867     /* 
1868      * if SIGTERM was ignored, NUKE IT! 
1869      */
1870     if (!killed)
1871     {
1872         kill(kill_pgrp_id, SIGKILL);    /* can't ignore this, turkey! */
1873         for (waitcount= 0; (!killed) && (waitcount < 5); ++waitcount)
1874         {
1875             if (kill(kill_pgrp_id, 0) == -1)    /* sig 0 checks pid only */
1876             {
1877                 /* kill failed, so group doesn't exist, any more */
1878                 killed = TRUE;
1879             }
1880             if (!killed)
1881             {
1882                 sleep(1);
1883             }
1884         }
1885     }
1886
1887     /*util_dprintf(2,"%s: %ld\n", (killed? "Killed":"COULD NOT KILL"), (long)pgid);*/
1888
1889     return killed? 0:-1;
1890 }
1891
1892
1893 /*
1894  * projectName may be NULL (signifies no project)
1895  */
1896 static int
1897 cgen_set_title(STRING projectName)
1898 {
1899     char        newTitle[256];
1900     strcpy(newTitle, "Code Generator ");
1901     if (projectName == NULL)
1902     {
1903         strcat(newTitle, "(No Project)");
1904     }
1905     else
1906     {
1907         sprintf(newTitle+strlen(newTitle), " - Project %s.bip",
1908                 projectName);
1909     }
1910
1911     XtVaSetValues(XtParent(AB_cgen_win),
1912         XmNtitle,       newTitle,
1913         NULL);
1914
1915     return 0;
1916 }
1917
1918
1919 /*
1920  * Sets the project dir: field to be the current directory
1921  * If dir is NULL, looks at CWD.
1922  */
1923 static int
1924 cgen_set_project_dir(STRING dir)
1925 {
1926     STRING      newDir= NULL;
1927     XmString    xmlabel= NULL;
1928
1929     if (dir != NULL)
1930     {
1931         newDir= dir;
1932     }
1933     else
1934     {
1935         newDir= ab_get_cur_dir();
1936     }
1937     xmlabel = XmStringCreateLocalized(newDir);
1938     XtVaSetValues(cur_dir_text,
1939                 XmNlabelString, xmlabel,
1940                 NULL);
1941     XmStringFree(xmlabel); xmlabel= NULL;
1942     return 0;
1943 }
1944
1945
1946 static int
1947 cgen_obj_name_changed_cb(ObjEvAttChangeInfo evInfo)
1948 {
1949     ABObj       project= evInfo->obj;
1950     char        newTitle[256];
1951
1952     if (   (obj_is_project(project))
1953         && ((evInfo->atts & OBJEV_ATT_NAME) != 0) 
1954         && (proj_get_project() == project) 
1955        )
1956     {
1957         /* the project's name changed.  Update the title bar */
1958         cgen_set_title(obj_get_name(project));
1959     }
1960
1961     return 0;
1962 }
1963
1964
1965 static int
1966 cgenP_abort(void)
1967 {
1968     /*util_dprintf(2, "GUI: aborting(%ld)\n", (long)actual_process_pgid);*/
1969     if (   (actual_process_pgid != INVALID_PID)
1970         && (abortingPID != actual_process_pgid) )
1971     {
1972         /* the process still exists, and it is not in the
1973          * process of being aborted. Abort it!
1974          */
1975         XtSetSensitive(abort_button, False);
1976         abortingPID = actual_process_pgid;      /* do first!, so we know.. */
1977         careful_kill_group(abortingPID);
1978         XtSetSensitive(abort_button, True);
1979     }
1980
1981     if (   (actual_process_pgid == INVALID_PID)
1982         && (abortingPID == INVALID_PID) )
1983     {
1984         /* There is no process to be aborted */
1985         goto_ready_state();
1986     }
1987
1988     return 0;
1989 }
1990
1991
1992 /*
1993  * projectName may be NULL (signifies no project)
1994  */
1995 static int
1996 set_props_proj_name(STRING projectName)
1997 {
1998     char        newProj[256];
1999
2000     if (projectName == NULL)
2001     {
2002         strcpy(newProj, "(No Project)");
2003     }
2004     else
2005     {
2006         sprintf(newProj, "%s.bip", projectName);
2007     }
2008
2009     XtVaSetValues(dtb_cgen_props_cgen_props_dlg.proj_name,
2010         XtVaTypedArg, XmNlabelString, XtRString,
2011         newProj, strlen(newProj)+1,
2012         NULL);
2013
2014     return 0;  
2015 }
2016  
2017 /*
2018  * obj-callback: object name has changed - update Prop Dialog lists
2019  *               Or project name has changed - update the Prop
2020  *               proj name.
2021  */
2022 static int
2023 obj_renamedOCB(
2024     ObjEvAttChangeInfo    info
2025 )
2026 {
2027     Widget      list = NULL;
2028     STRING      mod_name = NULL;
2029  
2030     if (AB_cgen_prop_dialog != NULL)
2031     {
2032         if (!obj_is_module(info->obj) && !obj_is_project(info->obj))
2033         {
2034             return 0;
2035         }
2036  
2037         if (   (obj_is_project(info->obj))
2038                 && ((info->atts & OBJEV_ATT_NAME) != 0))
2039         {
2040             /* the project's name changed.  Update prop sheet proj_name */
2041             set_props_proj_name(obj_get_name(info->obj));
2042         }
2043         else
2044         {
2045             mod_name = obj_get_name(info->obj);
2046             if (mod_name == NULL)
2047                 return -1;
2048  
2049             XtVaGetValues(AB_cgen_prop_dialog, XmNuserData, &list, NULL);
2050  
2051             /* A new module was created and named */
2052             if (info->old_name != NULL)
2053             {
2054                 ui_list_replace_item(list, istr_string(info->old_name), mod_name);
2055             }
2056         }
2057     }
2058
2059     return 0;
2060 }    
2061  
2062 /*
2063  * obj-callback: object is being destroyed - remove from CGen prop
2064  *              sheet list.
2065  */
2066 static int
2067 obj_destroyedOCB(
2068     ObjEvDestroyInfo    info
2069 )
2070 {
2071     Widget      list = NULL;
2072     STRING      mod_name = NULL;
2073  
2074     if (AB_cgen_prop_dialog != NULL)
2075     {
2076         if (!obj_is_module(info->obj))
2077             return 0;
2078
2079         mod_name = obj_get_name(info->obj);
2080         if (mod_name == NULL)
2081             return -1;
2082  
2083         XtVaGetValues(AB_cgen_prop_dialog, XmNuserData, &list, NULL);
2084         ui_list_delete_item(list, mod_name);
2085     }
2086  
2087     return 0;    
2088 }
2089
2090 /* 
2091  * obj-callback: Called when a new project is opened.
2092  */
2093 static int
2094 obj_updateOCB(
2095     ObjEvUpdateInfo     info
2096 )
2097 {
2098     Widget      list = NULL;
2099
2100     if (AB_cgen_prop_dialog != NULL)
2101     {
2102         if ( !obj_is_project(info->obj) &&
2103              !obj_is_module(info->obj)
2104            )
2105         {
2106             return 0;
2107         }
2108
2109         if (obj_is_project(info->obj))
2110         {
2111             /* the project's name changed.  Update prop sheet proj_name */
2112             set_props_proj_name(obj_get_name(info->obj));
2113         }
2114         
2115         XtVaGetValues(AB_cgen_prop_dialog, XmNuserData, &list, NULL);
2116         XmListDeleteAllItems(list);
2117         cgenP_init_props_module_list(list);
2118         
2119         /* Initialize the selected modules lists */
2120         if (CodeGenOptions.module_list != NULL)  /* the list that is applied */
2121         {
2122             strlist_destroy(CodeGenOptions.module_list);
2123             CodeGenOptions.module_list = NULL;
2124         }
2125         if (module_list != NULL)        /* the list that reflects what
2126                                          * is currently selected */
2127         {
2128             strlist_destroy(module_list);
2129             module_list = strlist_create(); 
2130         }
2131     }
2132
2133     return 0;
2134 }
2135
2136 int
2137 cgenP_sync_up_dir(
2138 )
2139 {
2140     STRING      cmd = NULL;
2141     STRING      request_dir = NULL;
2142     int         cmd_size = 0;
2143
2144     request_dir = ab_get_cur_dir();
2145     if (!util_strempty(request_dir))
2146     {
2147         cmd_size = strlen("cd ") + strlen(request_dir) + 2;
2148         cmd = (STRING) XtMalloc(cmd_size);
2149         strcpy(cmd, "cd ");
2150         strcat(cmd, request_dir);
2151         strcat(cmd, "\n");
2152         DtTermSubprocSend(input_termWidget, (unsigned char*)cmd, strlen(cmd));
2153     }
2154     return 0;
2155 }
2156
2157
2158 static int
2159 do_user_action(CG_GOAL goal, CG_SUBCOMMAND cmd)
2160 {
2161     int                 return_value = 0;
2162     int                 rc = 0;                         /* return code */
2163     BOOL                doAction = TRUE;
2164     DTB_MODAL_ANSWER    answer = DTB_ANSWER_NONE;
2165     ABObj               project = proj_get_project();
2166     Widget              dlg = NULL;
2167
2168     user_goal = goal;
2169     assert(cmd == CG_CMD_UNDEF);        /* only startup implemented, here */
2170
2171     /***** SEE IF cc IS ON THE PATH *****/
2172
2173     if ((rc = check_path()) < 0)
2174     {
2175         return_value = rc;
2176         goto epilogue;
2177     }
2178
2179
2180     /*
2181      * See if we need to build the executable
2182      */
2183     if (goal == CG_GOAL_RUN)
2184     {
2185         char    exeName[MAXPATHLEN+1];
2186         ABObj   project = proj_get_project();
2187         STRING  projName = NULL;
2188         if ((project != NULL) && ((projName = obj_get_name(project)) != NULL))
2189         {
2190             sprintf(exeName, "./%s", projName);
2191             if (!util_file_exists(exeName))
2192             {
2193                 goal = CG_GOAL_UNDEF;   /* can't run it - it don't exist! */
2194                 dtb_cgen_win_no_exe_msg_initialize(
2195                         &dtb_cgen_win_no_exe_msg);
2196                 answer = dtb_show_modal_message(AB_cgen_win,
2197                         &dtb_cgen_win_no_exe_msg, 
2198                         NULL, NULL, &dlg);
2199                 if (answer == DTB_ANSWER_ACTION1)       /* Build It */
2200                 {
2201                     goal = CG_GOAL_MAKE_AND_RUN;
2202                 }
2203             }
2204         }
2205     }
2206
2207     if (goal == CG_GOAL_UNDEF)
2208     {
2209         return -1;
2210     }
2211     user_goal = goal;
2212
2213
2214     /***** SEE IF THERE ARE UNSAVED EDITS *****/
2215
2216         if ( (goal != CG_GOAL_RUN) && proj_check_unsaved_edits(project) )
2217         {
2218             BOOL        doSave = FALSE;
2219
2220             if (all_files_exist(project))
2221             {
2222                 dtb_cgen_win_query_save_or_gen_old_msg_initialize(
2223                         &dtb_cgen_win_query_save_or_gen_old_msg);
2224                 answer = dtb_show_modal_message(AB_cgen_win,
2225                         &dtb_cgen_win_query_save_or_gen_old_msg, 
2226                         NULL, NULL, &dlg);
2227                 switch (answer)
2228                 {
2229                     case DTB_ANSWER_ACTION1:    /* save */
2230                         doSave = TRUE;
2231                     break;
2232
2233                     case DTB_ANSWER_ACTION2:    /* gen old */
2234                     break;
2235
2236                     default:
2237                         doAction = FALSE;
2238                     break;
2239                 }
2240             }
2241             else
2242             {
2243                 dtb_cgen_win_query_save_or_abort_msg_initialize(
2244                         &dtb_cgen_win_query_save_or_abort_msg);
2245                 answer = dtb_show_modal_message(AB_cgen_win,
2246                         &dtb_cgen_win_query_save_or_abort_msg, 
2247                         NULL, NULL, &dlg);
2248                 switch (answer)
2249                 {
2250                     case DTB_ANSWER_ACTION1:    /* save */
2251                         doSave = TRUE;
2252                     break;
2253
2254                     default:
2255                         doAction = FALSE;
2256                     break;
2257                 }
2258             }
2259
2260             if (doSave)
2261             {
2262                 doAction = FALSE;       /* will be done by save_done_cb */
2263                 proj_save_needed(save_done_cb);
2264             }
2265         } /* proj_check_unsaved_edits() */
2266
2267     if (doAction)
2268     {
2269         exec_next_command(CG_CMD_UNDEF, 0);
2270     }
2271
2272 epilogue:
2273     return return_value;
2274 }
2275
2276
2277 static int
2278 save_done_cb(int status)
2279 {
2280     if (status < 0)
2281     {
2282         return 0;
2283     }
2284
2285     exec_next_command(CG_CMD_UNDEF, 0);
2286     return 0;
2287 }
2288
2289
2290 static int
2291 check_path()
2292 {
2293     int         return_value = 0;
2294     int         rc = 0;         /* return code */
2295     BOOL        keepAsking = TRUE;
2296
2297     if (keepAsking)
2298     {
2299         static BOOL     allowWarnUserAboutCc = TRUE;
2300         static STRING   ccCmdList[] =
2301         {
2302             "cc", 
2303             "/opt/SUNWspro/bin/cc",
2304             "/usr/dist/exe/cc", 
2305             "/usr/dist/local/exe/cc",
2306             NULL
2307         };
2308         if ((rc = check_path_to_cmd(ccCmdList, &allowWarnUserAboutCc)) < 0)
2309         {
2310             keepAsking = FALSE;
2311         }
2312     }
2313     
2314     if (keepAsking)
2315     {
2316         static BOOL             allowWarnUserAboutSh = TRUE;
2317         static STRING   shCmdList[] =
2318         {
2319             "sh",
2320             "/bin/sh",
2321             "/usr/bin/sh",
2322             "/sbin/sh",
2323             "/usr/dist/exe/sh",
2324             "/usr/dist/local/exe/sh",
2325             NULL
2326         };
2327         if ((rc = check_path_to_cmd(shCmdList, &allowWarnUserAboutSh)) < 0)
2328         {
2329             keepAsking = FALSE;
2330         }
2331     }
2332
2333     if (keepAsking)
2334     {
2335         static BOOL             allowWarnUserAboutRm = TRUE;
2336         static STRING   rmCmdList[] =
2337         {
2338             "rm",
2339             "/bin/rm",
2340             "/usr/bin/rm",
2341             NULL
2342         };
2343         if ((rc = check_path_to_cmd(rmCmdList, &allowWarnUserAboutRm)) < 0)
2344         {
2345             keepAsking = FALSE;
2346         }
2347     }
2348
2349     if ((return_value >= 0) && (!keepAsking))
2350     {
2351         return_value = -1;
2352     }
2353
2354     return return_value;
2355 }
2356
2357
2358 /*
2359  * Returns <0 if the user cancelled
2360  */
2361 static int
2362 check_path_to_cmd(STRING *cmdList, BOOL *allowWarnUserInOut)
2363 {
2364 #define allowWarnUser (*allowWarnUserInOut)
2365     int                 return_value = 0;
2366     BOOL                userCancelled = FALSE;
2367     int                 rc = 0;         /* return code */
2368     STRING              foundCmd = NULL;
2369     DTB_MODAL_ANSWER    answer = DTB_ANSWER_NONE;
2370     Widget              dlg = NULL;
2371     XmString            xmMsg = NULL;
2372     STRING              nopathCmd = NULL;       /* cmd with no path */
2373     
2374     nopathCmd = strrchr(cmdList[0], '/');
2375     if (nopathCmd == NULL)
2376     {
2377         nopathCmd = cmdList[0];
2378     }
2379
2380     if (allowWarnUser)
2381     {
2382         if ((rc = select_command(cmdList, &foundCmd)) < 0)
2383         {
2384             /* command not found, anywhere! */
2385             return rc;
2386         }
2387
2388         if (!util_streq(foundCmd, nopathCmd))
2389         {
2390             /* not on path! */
2391             char        dirName[MAXPATHLEN+1];
2392             char        *slashPtr = strrchr(foundCmd, '/');
2393             int         dirNameLen = 0;
2394             char        buffer[16384];
2395             STRING      oldPath = NULL;
2396             *buffer = 0;
2397
2398             oldPath = cgenP_get_env_var("PATH");
2399             if (oldPath == NULL)
2400             {
2401                 oldPath = "";
2402             }
2403
2404             if (slashPtr != NULL)
2405             {
2406                 dirNameLen = (int)(slashPtr - foundCmd);
2407                 util_strncpy(dirName, foundCmd, dirNameLen+1);
2408                 /*util_dprintf(2, "directory: '%s'\n", dirName);*/
2409
2410                 sprintf(buffer, catgets(Dtb_project_catd, 100, 52,
2411                     "Your PATH does not contain the command %s.\n"
2412                     "In order to access this command, may I append this\n"
2413                     "directory to your path?:\n"
2414                     "\n"
2415                     "    %s"),
2416                         nopathCmd, dirName);
2417                     
2418                 xmMsg = XmStringCreateLocalized(buffer);
2419                 dtb_cgen_win_modify_path_msg_initialize(
2420                         &dtb_cgen_win_modify_path_msg);
2421                 answer = dtb_show_modal_message(AB_cgen_win,
2422                         &dtb_cgen_win_modify_path_msg, 
2423                         xmMsg, NULL, &dlg);
2424                 XmStringFree(xmMsg); xmMsg = NULL;
2425
2426                 switch (answer)
2427                 {
2428                     case DTB_ANSWER_ACTION1:    /* Yes */
2429                         sprintf(buffer, "%s:%s", oldPath, dirName);
2430                         cgenP_put_env_var("PATH", buffer);
2431                     break;
2432
2433                     case DTB_ANSWER_ACTION2:    /* No */
2434                         allowWarnUser = FALSE;
2435                     break;
2436
2437                     case DTB_ANSWER_CANCEL:
2438                         userCancelled = TRUE;
2439                     break;
2440                 }
2441             }
2442         }
2443     }
2444
2445     if ((return_value >= 0) && userCancelled)
2446     {
2447         return_value = -1;
2448     }
2449     return return_value;
2450 #undef allowWarnUser
2451 }
2452
2453
2454 /*
2455  * Makes sure that all of the files for the project at least exist on 
2456  * the disk.
2457  */
2458 static BOOL
2459 all_files_exist(ABObj project)
2460 {
2461     BOOL                allFilesExist = TRUE;
2462     STRING              fileName = NULL;
2463     AB_TRAVERSAL        trav;
2464     ABObj               module = NULL;
2465
2466     if (   ((fileName= obj_get_file(project)) == NULL)
2467         || (!util_file_exists(fileName)) )
2468     {
2469         allFilesExist = FALSE;
2470         abobj_set_save_needed(project, TRUE);
2471     }
2472
2473     for (trav_open(&trav, project, AB_TRAV_MODULES); 
2474          ((module = trav_next(&trav)) != NULL); )
2475     {
2476         if (   ((fileName= obj_get_file(module)) == NULL)
2477             || (!util_file_exists(fileName)) )
2478         {
2479             allFilesExist = FALSE;
2480             abobj_set_save_needed(module, TRUE);
2481         }
2482     }
2483     trav_close(&trav);
2484
2485     return allFilesExist;
2486 }
2487
2488
2489 static STRING
2490 cgenP_get_env_var(STRING varName)
2491 {
2492     STRING value = NULL;
2493     
2494     if (user_env_vars != NULL)
2495     {
2496         value = (STRING)strlist_get_str_data(user_env_vars, varName);
2497     }
2498     if (value == NULL)
2499     {
2500         value = getenv(varName);
2501     }
2502     return value;
2503 }
2504
2505
2506 /*
2507  * Creates a duplicate of the value.
2508  */
2509 static int
2510 cgenP_put_env_var(STRING varName, STRING varValue)
2511 {
2512     int         strIndex = -1;
2513     STRING      oldValue = NULL;
2514     STRING      newValue = NULL;
2515
2516     if (user_env_vars == NULL)
2517     {
2518         user_env_vars = strlist_create();
2519     }
2520
2521     strIndex = strlist_get_str_index(user_env_vars, varName);
2522     if (strIndex >= 0)
2523     {
2524         strlist_get_str(user_env_vars, strIndex, (void **)&oldValue);
2525         if (oldValue != NULL)
2526         {
2527             util_free(oldValue);
2528         }
2529         strlist_remove_index(user_env_vars, strIndex); strIndex = -1;
2530     }
2531
2532     newValue = strdup(varValue);
2533     strlist_add_str(user_env_vars, varName, newValue);
2534     return 0;
2535 }
2536
2537
2538 static int
2539 check_makefile(BOOL *continueOutPtr)
2540 {
2541     static BOOL allowDestroyMakefile = TRUE;
2542     int         return_value = 0;
2543     int         rc = 0;                 /* return code */
2544     BOOL        makefileExists = FALSE;
2545     BOOL        makefileIsOK = FALSE;
2546     DTB_MODAL_ANSWER    answer = DTB_ANSWER_NONE;
2547     Widget              dlg = NULL;
2548     BOOL                doDestroyMakefile = FALSE;
2549     BOOL                doGenMakefile = FALSE;
2550     BOOL                makeStarted = FALSE;
2551
2552     *continueOutPtr = TRUE;
2553
2554     /*
2555      * Look for makefile
2556      */
2557     rc = cgenP_makefile_is_for_project("makefile", proj_get_project());
2558     if (rc >= 0)
2559     {
2560         makefileExists = makefileIsOK = TRUE;
2561     }
2562     else if (rc  == ERR_OPEN)
2563     {
2564         makefileExists = FALSE;
2565         makefileIsOK = FALSE;
2566     }
2567     else
2568     {
2569         makefileExists = TRUE;
2570         makefileIsOK = FALSE;
2571     }
2572     if (makefileIsOK)
2573     {
2574         goto epilogue;
2575     }
2576
2577
2578     /*
2579      * Look for Makefile
2580      */
2581     if (!makefileExists)
2582     {
2583         rc = cgenP_makefile_is_for_project("Makefile", proj_get_project());
2584         if (rc >= 0)
2585         {
2586             makefileExists = makefileIsOK = TRUE;
2587         }
2588         else if (rc  == ERR_OPEN)
2589         {
2590             makefileExists = FALSE;
2591             makefileIsOK = FALSE;
2592         }
2593         else
2594         {
2595             makefileExists = TRUE;
2596             makefileIsOK = FALSE;
2597         }
2598         if (makefileIsOK)
2599         {
2600             goto epilogue;
2601         }
2602     }
2603
2604     /*
2605      * Display a warning dialog
2606      */
2607     if (!makefileExists)
2608     {
2609         dtb_cgen_win_no_makefile_msg_initialize(&dtb_cgen_win_no_makefile_msg);
2610         answer = dtb_show_modal_message(AB_cgen_win,
2611         &dtb_cgen_win_no_makefile_msg, 
2612             NULL, NULL, &dlg);
2613         switch (answer)
2614         {
2615             case DTB_ANSWER_ACTION1:    /* Yes */
2616                 doGenMakefile = TRUE;
2617             break;
2618
2619             case DTB_ANSWER_ACTION2:    /* No */
2620                 doGenMakefile = FALSE;
2621             break;
2622
2623             case DTB_ANSWER_CANCEL:
2624                 doGenMakefile = FALSE;
2625                 *continueOutPtr = FALSE;
2626             break;
2627         }
2628     }
2629     else if ((!makefileIsOK) && (allowDestroyMakefile))
2630     {
2631         dtb_cgen_win_wrong_makefile_msg_initialize(
2632                 &dtb_cgen_win_wrong_makefile_msg);
2633         answer = dtb_show_modal_message(AB_cgen_win,
2634                 &dtb_cgen_win_wrong_makefile_msg, 
2635                 NULL, NULL, &dlg);
2636         switch (answer)
2637         {
2638             case DTB_ANSWER_ACTION1:    /* Yes */
2639                 doDestroyMakefile = TRUE;
2640                 doGenMakefile = TRUE;
2641             break;
2642
2643             case DTB_ANSWER_ACTION2:    /* No */
2644                 doGenMakefile = FALSE;
2645             break;
2646
2647             case DTB_ANSWER_ACTION3:    /* Never */
2648                 doGenMakefile = FALSE;
2649                 allowDestroyMakefile = FALSE;
2650             break;
2651
2652             case DTB_ANSWER_CANCEL:
2653                 doGenMakefile = FALSE;
2654                 *continueOutPtr = FALSE;
2655             break;
2656         }
2657     }
2658
2659     /*
2660      * Perform the actions specified by the user
2661      */
2662     if (doDestroyMakefile)
2663     {
2664         destroy_makefile();
2665     }
2666     if (doGenMakefile)
2667     {
2668         *continueOutPtr = FALSE;        /* will restart when dtcodegen done */
2669         if ((rc = exec_generate_main()) >= 0)
2670         {
2671             makeStarted = TRUE;
2672             return_value = rc;
2673             goto epilogue;
2674         }
2675     }
2676
2677
2678 epilogue:
2679     if (! (makeStarted || (*continueOutPtr)) )
2680     {
2681         /* we're not doing anything */
2682         goto_ready_state();
2683     }
2684
2685     return return_value;
2686 }
2687
2688
2689 static int
2690 destroy_makefile()
2691 {
2692     destroy_links_to_file("makefile");
2693     destroy_links_to_file("Makefile");
2694     return 0;
2695 }
2696
2697
2698 static int
2699 destroy_links_to_file(STRING fileName)
2700 {
2701     int                 return_value = 0;
2702     struct stat         doomedFileInfo;
2703     struct stat         curFileInfo;
2704     DIR                 *dir = NULL;
2705     struct dirent       *dirEntry = NULL;
2706     StringList          doomedFiles = strlist_create();
2707     int                 i = 0;
2708     int                 numFiles = 0;
2709
2710     strlist_add_str(doomedFiles, fileName, NULL);
2711     if (stat(fileName, &doomedFileInfo) != 0)
2712     {
2713         return ERR_OPEN;
2714     }
2715
2716     dir = opendir(".");
2717     if (dir == NULL)
2718     {
2719         return ERR_INTERNAL;
2720     }
2721
2722     while ((dirEntry= readdir(dir)) != NULL)
2723     {
2724         if (stat(dirEntry->d_name, &curFileInfo) != 0)
2725         {
2726             return_value = -1;
2727             break;
2728         }
2729         if (   (doomedFileInfo.st_dev == curFileInfo.st_dev)
2730             && (doomedFileInfo.st_ino == curFileInfo.st_ino) )
2731         {
2732             /* files are the same! */
2733             strlist_add_str(doomedFiles, dirEntry->d_name, NULL);
2734         }
2735     }
2736
2737     closedir(dir);
2738
2739     /*
2740      * We've built a list of all filenames in the current directory that
2741      * refer to the given file.
2742      */
2743     numFiles = strlist_get_num_strs(doomedFiles);
2744     for (i = 0; i < numFiles; ++i)
2745     {
2746         move_file_to_backup(strlist_get_str(doomedFiles, i, NULL));
2747     }
2748
2749     if (dir != NULL)
2750     {
2751         closedir(dir); dir = NULL;
2752     }
2753     strlist_destroy(doomedFiles);
2754     return return_value;
2755 }
2756
2757
2758 static int
2759 move_file_to_backup(STRING fileName)
2760 {
2761     char        bakNameBuf[MAXPATHLEN+1];
2762     int         fileNameLen = strlen(fileName);
2763
2764     if ((fileNameLen >= 4) && (strcmp(fileName+fileNameLen-4, ".BAK") == 0))
2765     {
2766         /* Don't make a .BAK.BAK file */
2767         return 0;
2768     }
2769
2770     sprintf(bakNameBuf, "%s.BAK", fileName);
2771     unlink(bakNameBuf);
2772     return rename(fileName, bakNameBuf);
2773 }
2774
2775
2776 static int
2777 cgenP_makefile_is_for_project(STRING fileName, ABObj project)
2778 {
2779     int                 return_value = 0;
2780     FILE                *makeFile = NULL;
2781     StringList          genFileNames = strlist_create();
2782     ABObj               module = NULL;
2783     int                 i = 0;
2784     int                 numFiles = 0;
2785     AB_TRAVERSAL        trav;
2786     assert((project == NULL) || obj_is_project(project));
2787
2788     if (project == NULL)
2789     {
2790         goto epilogue;
2791     }
2792
2793     makeFile = util_fopen_locked(fileName, "r");
2794     if (makeFile == NULL)
2795     {
2796         return_value = ERR_OPEN;
2797         goto epilogue;
2798     }
2799
2800     /*
2801      * Create list of file names
2802      */
2803     add_obj_file_name(genFileNames, project, NULL);
2804     for (trav_open(&trav, project, AB_TRAV_MODULES);
2805         (module = trav_next(&trav)) != NULL; )
2806     {
2807         add_obj_file_name(genFileNames, module, "_ui");
2808     }
2809     trav_close(&trav);
2810
2811     if (!strings_exist_in_file(genFileNames, makeFile))
2812     {
2813         return_value = ERR;
2814     }
2815
2816 epilogue:
2817     util_fclose(makeFile);
2818     strlist_destroy(genFileNames);
2819     return return_value;
2820 }
2821
2822
2823 static int
2824 add_obj_file_name(StringList fileNames, ABObj obj, STRING suffix)
2825 {
2826     STRING      objName = NULL;
2827     char        fileName[MAXPATHLEN+1];
2828     *fileName = 0;
2829
2830     if (obj == NULL)
2831     {
2832         return -1;
2833     }
2834     if ((objName = obj_get_name(obj)) == NULL)
2835     {
2836         return -1;
2837     }
2838
2839     if (suffix == NULL)
2840     {
2841         suffix = Util_empty_string;
2842     }
2843     sprintf(fileName, "%s%s", objName, suffix);
2844     strlist_add_str(fileNames, fileName, NULL);
2845     return 0;
2846 }
2847
2848
2849 static BOOL
2850 strings_exist_in_file(StringList strings, FILE *file)
2851 {
2852 #define fast_strneq(s1,s2,n) \
2853             (((*(s1)) == (*(s2))) && (strncmp(s1,s2,n) == 0))
2854     BOOL        stringsExist = FALSE;
2855     STRING      *stringsArray = NULL;
2856     int         *stringsLenArray = NULL;
2857     int         maxFileNameLen = 0;
2858     int         curFileNameLen = 0;
2859     STRING      curFileName = NULL;
2860     char        buf[8193];
2861     int         numStrings = 0;
2862     int         numStringsFound = 0;
2863     int         i = 0;
2864     int         maxBufLen = sizeof(buf)-1;
2865     int         c = 0;
2866     int         bufLen = 0;
2867     BOOL        *stringExists = NULL;
2868     char        *stringStart = NULL;
2869     *buf = 0;
2870
2871     numStrings = strlist_get_num_strs(strings);
2872
2873     /*
2874      * Convert strings list to arrays, to avoid 750,000 calls to istr_string()
2875      */
2876     stringsArray = (STRING*)util_malloc(numStrings * sizeof(STRING));
2877     stringsLenArray = (int*)util_malloc(numStrings * sizeof(int));
2878     stringExists = (BOOL*)util_malloc(numStrings * sizeof(BOOL));
2879     if (   (stringsArray == NULL) 
2880         || (stringsLenArray == NULL) 
2881         || (stringExists == NULL) )
2882     {
2883         goto epilogue;
2884     }
2885     for (i = 0; i < numStrings; ++i)
2886     {
2887         stringsArray[i] = strlist_get_str(strings, i, NULL);
2888         stringsLenArray[i] = strlen(stringsArray[i]);
2889         stringExists[i] = FALSE;
2890     }
2891
2892
2893     /*
2894      * Determine the longest file name
2895      */
2896     for (i = 0; i < numStrings; ++i)
2897     {
2898         curFileName = stringsArray[i];
2899         curFileNameLen = stringsLenArray[i];
2900         maxFileNameLen = util_max(maxFileNameLen, curFileNameLen);
2901     }
2902     assert(maxFileNameLen < sizeof(buf));
2903
2904     /*
2905      * scan the file
2906      */
2907     rewind(file);
2908     bufLen = fread((void *)buf, 1, maxFileNameLen -1, file);
2909     while ((c = fgetc(file)) != EOF)
2910     {
2911         if (bufLen >= maxBufLen)
2912         {
2913             /* we've reached the end of the buffer */
2914             memmove(buf, buf + maxBufLen - maxFileNameLen, maxFileNameLen);
2915             bufLen = maxFileNameLen;
2916         }
2917         buf[bufLen++] = c;
2918
2919         for (i = 0; i < numStrings; ++i)
2920         {
2921             if (stringExists[i])
2922             {
2923                 continue;
2924             }
2925             stringStart = buf + bufLen - stringsLenArray[i];
2926             if (fast_strneq(stringStart, stringsArray[i], stringsLenArray[i]))
2927             {
2928                 stringExists[i] = TRUE;
2929                 if (++numStringsFound >= numStrings)
2930                 {
2931                     /*
2932                      * Instead of a loop-control variable that must get
2933                      * checked on each iteration, we use a goto to speed
2934                      * things up, dramatically.
2935                      */
2936                     goto exit_file_loop;
2937                     break;
2938                 }
2939             }
2940         }
2941     }
2942 exit_file_loop:
2943
2944     /*
2945      * See if they were all found
2946      */
2947     stringsExist = TRUE;
2948     for (i = 0; i < numStrings; ++i)
2949     {
2950         if (!stringExists[i])
2951         {
2952             stringsExist = FALSE;
2953             if (!debugging())
2954             {
2955                 break;
2956             }
2957 #ifdef DEBUG
2958             util_dprintf(1, "Not in makefile: '%s'\n", stringsArray[i]);
2959 #endif /* DEBUG */
2960         }
2961     }
2962
2963 epilogue:
2964     util_free(stringsArray);
2965     util_free(stringsLenArray);
2966     util_free(stringExists);
2967     return stringsExist;
2968 #undef fast_strneq
2969 }
2970
2971
2972 static int
2973 select_command(STRING *cmdList, STRING *cmdOutPtr)
2974 {
2975     int         i = 0;
2976     int         cmdIndex = -1;
2977     STRING      path = NULL;
2978
2979     *cmdOutPtr = NULL;
2980     path = cgenP_get_env_var("PATH");
2981
2982     for (i = 0; (cmdIndex < 0) && (cmdList[i] != NULL); ++i)
2983     {
2984         if ((strlen(cmdList[i]) > 0) && (command_exists(cmdList[i], path)))
2985         {
2986             cmdIndex = i;
2987             break;
2988         }
2989     }
2990
2991     if (cmdIndex >= 0)
2992     {
2993         *cmdOutPtr = cmdList[cmdIndex];
2994     }
2995     return cmdIndex;
2996 }
2997
2998
2999 static BOOL
3000 command_exists(STRING cmd, STRING path)
3001 {
3002     static uid_t        euid = (uid_t)-1;
3003     static uid_t        egid = (uid_t)-1;
3004     BOOL        cmdExists = FALSE;
3005     char        szCurrentPath[MAXPATHLEN+1];
3006     int         iCurrentPathStart = -1;
3007     int         iCurrentPathLen = -1;
3008     int         iPathLen = -1;
3009     int         i = 0;
3010     int         iExeNameLen = strlen(cmd);
3011     BOOL        moreDirs = FALSE;
3012     *szCurrentPath = 0;
3013
3014     if (euid == (uid_t)-1)
3015     {
3016         euid = geteuid();
3017         egid = getegid();
3018     }
3019
3020     /*
3021      * Check for abolute path to command
3022      */
3023     if (   (strncmp(cmd, "/", 1) == 0)
3024         || (strncmp(cmd, "./", 2) == 0)
3025         || (strncmp(cmd, "../", 3) == 0)
3026        )
3027     {
3028         /* an absolute path */
3029         cmdExists = path_is_executable(cmd, euid, egid);
3030         goto epilogue;
3031     }
3032
3033     /*
3034      * Search path for command
3035      */
3036     iCurrentPathStart = 0;
3037     iCurrentPathLen = 0;
3038     iPathLen = strlen(path);
3039     moreDirs = TRUE;
3040
3041     while ((!cmdExists) && (moreDirs))
3042     {
3043         /* find beginning of dir name (skip ':') */
3044         while (   (iCurrentPathStart < iPathLen) 
3045                && (path[iCurrentPathStart] == ':'))
3046         {
3047             ++iCurrentPathStart;        /* skip : */
3048         }
3049         if (iCurrentPathStart >= iPathLen)
3050         {
3051             moreDirs = FALSE;
3052             continue;
3053         }
3054
3055         /* find end of dir name */
3056         for (i= iCurrentPathStart; (i < iPathLen) && (path[i] != ':'); )
3057         {
3058             ++i;
3059         }
3060         iCurrentPathLen= i - iCurrentPathStart;
3061
3062         /* make sure path to executable is not too long */
3063         if ((iCurrentPathLen + iExeNameLen + 2) > MAXPATHLEN)
3064         {
3065             iCurrentPathLen= MAXPATHLEN - (iExeNameLen + 2);
3066         }
3067
3068         /* create a possible path to the executable */
3069         util_strncpy(szCurrentPath, &path[iCurrentPathStart], 
3070                                 iCurrentPathLen+1);
3071         strcat(szCurrentPath, "/");
3072         strcat(szCurrentPath, cmd);
3073
3074         /* see if the executable exists (and we can execute it) */
3075         if (path_is_executable(szCurrentPath, euid, egid))
3076         {
3077             cmdExists = True;
3078         }
3079
3080         /* skip past the current directory name */
3081         iCurrentPathStart += iCurrentPathLen;
3082     } /* while !cmdExists */
3083
3084 epilogue:
3085     return cmdExists;
3086 }
3087
3088 /*
3089  * returns False is path does not exist or is not executable
3090  */
3091 static Boolean
3092 path_is_executable(
3093     char        *path,
3094     uid_t       euid,
3095     gid_t       egid
3096 )
3097 {
3098     Boolean     bExecutable= False;
3099     struct stat sStat;
3100
3101     /* util_dprintf(3, "path_is_executable(%s)\n", path); */
3102     if (stat(path, &sStat) == 0)
3103     {
3104         Boolean bDetermined= False;
3105
3106         if (!bDetermined)
3107         {
3108             if (!S_ISREG(sStat.st_mode))
3109             {
3110                 /* not a regular file */
3111                 bDetermined= True;
3112                 bExecutable= False;
3113             }
3114         }
3115
3116         if (!bDetermined)
3117         {
3118             if (   (euid == 0) 
3119                 && (   ((sStat.st_mode & S_IXOTH) != 0)
3120                     || ((sStat.st_mode & S_IXGRP) != 0)
3121                     || ((sStat.st_mode & S_IXUSR) != 0) )
3122                )
3123             {
3124                 bDetermined= True;
3125                 bExecutable= True;
3126             }
3127         }
3128
3129         if (!bDetermined)
3130         {
3131             if (   (((sStat.st_mode & S_IXOTH) != 0)    )
3132                 || (((sStat.st_mode & S_IXGRP) != 0) && (sStat.st_gid == egid))
3133                 || (((sStat.st_mode & S_IXUSR) != 0) && (sStat.st_gid == euid))
3134                )
3135             {
3136                 bDetermined= True;
3137                 bExecutable= True;
3138             }
3139         }
3140     } /* if stat */
3141
3142     return bExecutable;
3143 }
3144
3145
3146 /*
3147  * This routine must be identical to that in src/abmf/obj_names.c. If
3148  * either this one or the other one is changed, copy the routine to the
3149  * other file.
3150  */
3151 static STRING
3152 cvt_type_to_ident(STRING type, STRING identBuf, int identBufSize)
3153 {
3154     int         typeOff = 0;
3155     int         identOff = 0;
3156     int         typeChar = -1;
3157     int         typeLen = util_strlen(type);
3158     int         identMaxLen = identBufSize-1;
3159     int         lastIdentChar = -1;
3160     BOOL        lastTypeCharWasUpper = FALSE;
3161
3162     for (typeOff = 0; 
3163             (typeOff < typeLen) && (identOff < (identMaxLen-1)); ++typeOff)
3164     {
3165         typeChar = type[typeOff];
3166         if (isupper(typeChar))
3167         {
3168             if (   (lastIdentChar != '_') 
3169                 && (!lastTypeCharWasUpper)
3170                 && (lastIdentChar != -1)
3171                )
3172             {
3173                 lastIdentChar = identBuf[identOff++] = '_';
3174             }
3175             if (identOff < (identMaxLen-1))
3176             {
3177                 lastIdentChar = identBuf[identOff++] = tolower(typeChar);
3178             }
3179             lastTypeCharWasUpper = TRUE;
3180         }
3181         else
3182         {
3183             lastIdentChar = identBuf[identOff++] = typeChar;
3184             lastTypeCharWasUpper = FALSE;
3185         }
3186     }
3187     identBuf[identOff] = 0;
3188
3189     return identBuf;
3190 }
3191
3192
3193 /*
3194  * util_fdsync() syncs the data and IO pending on an open file descriptor
3195  * out to the physical device, pipe, stream, or whatever.
3196  *
3197  * REMIND: move this to libAButil
3198  */
3199 #ifdef __cplusplus
3200 extern "C" {
3201 #endif
3202
3203 extern int fsync(int fd);               /* non-POSIX function */
3204
3205 #ifdef __cplusplus
3206 } //extern "C"
3207 #endif
3208
3209 static int
3210 util_fdsync(int fd)
3211 {
3212     BOOL        ok = FALSE;     /* OK if either sync or datasync works */
3213     if (fsync(fd) == 0)
3214     {
3215         ok = TRUE;
3216     }
3217
3218     return ok?0:-1;
3219 }
3220