BRL-CAD
editit.c
Go to the documentation of this file.
1 /* E D I T I T . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2008-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @file libged/editit.c
21  *
22  * The editit function.
23  *
24  */
25 
26 #include "common.h"
27 
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif
34 
35 #ifdef HAVE_SYS_WAIT_H
36 # include <sys/wait.h>
37 #endif
38 
39 #include "ged.h"
40 
41 #define WIN_EDITOR "\"c:/Program Files/Windows NT/Accessories/wordpad\""
42 #define MAC_EDITOR "/Applications/TextEdit.app/Contents/MacOS/TextEdit"
43 #define EMACS_EDITOR "emacs"
44 #define NANO_EDITOR "nano"
45 #define VIM_EDITOR "vim"
46 #define VI_EDITOR "vi"
47 
48 int
49 _ged_editit(char *editstring, const char *filename)
50 {
51 #ifdef HAVE_UNISTD_H
52  int xpid = 0;
53  int status = 0;
54 #endif
55  int pid = 0;
56  char **avtmp = (char **)NULL;
57  const char *terminal = (char *)NULL;
58  const char *terminal_opt = (char *)NULL;
59  const char *editor = (char *)NULL;
60  const char *editor_opt = (char *)NULL;
61  const char *file = (const char *)filename;
62 
63 #if defined(SIGINT) && defined(SIGQUIT)
64  void (*s2)();
65  void (*s3)();
66 #endif
67 
68  if (!file) {
69  bu_log("INTERNAL ERROR: editit filename missing\n");
70  return 0;
71  }
72 
73  /* convert the edit string into pieces suitable for arguments to execlp */
74 
75  if (editstring != NULL) {
76  avtmp = (char **)bu_calloc(5, sizeof(char *), "ged_editit: editstring args");
77  bu_argv_from_string(avtmp, 4, editstring);
78 
79  if (avtmp[0] && !BU_STR_EQUAL(avtmp[0], "(null)"))
80  terminal = avtmp[0];
81  if (avtmp[1] && !BU_STR_EQUAL(avtmp[1], "(null)"))
82  terminal_opt = avtmp[1];
83  if (avtmp[2] && !BU_STR_EQUAL(avtmp[2], "(null)"))
84  editor = avtmp[2];
85  if (avtmp[3] && !BU_STR_EQUAL(avtmp[3], "(null)"))
86  editor_opt = avtmp[3];
87  } else {
88  editor = getenv("EDITOR");
89 
90  /* still unset? try windows */
91  if (!editor || editor[0] == '\0') {
92  if (bu_file_exists(WIN_EDITOR, NULL)) {
93  editor = WIN_EDITOR;
94  }
95  }
96 
97  /* still unset? try mac os x */
98  if (!editor || editor[0] == '\0') {
99  if (bu_file_exists(MAC_EDITOR, NULL)) {
100  editor = MAC_EDITOR;
101  }
102  }
103 
104  /* still unset? try emacs */
105  if (!editor || editor[0] == '\0') {
106  editor = bu_which(EMACS_EDITOR);
107  }
108 
109  /* still unset? try nano */
110  if (!editor || editor[0] == '\0') {
111  editor = bu_which(NANO_EDITOR);
112  }
113 
114  /* still unset? try vim */
115  if (!editor || editor[0] == '\0') {
116  editor = bu_which(VIM_EDITOR);
117  }
118 
119  /* still unset? As a last resort, go with vi -
120  * vi is part of the POSIX standard, which is as
121  * close as we can get currently to an editor
122  * that should always be present:
123  * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html */
124  if (!editor || editor[0] == '\0') {
125  editor = bu_which(VI_EDITOR);
126  }
127  }
128 
129  if (!editor) {
130  bu_log("INTERNAL ERROR: editit editor missing\n");
131  return 0;
132  }
133 
134  /* print a message to let the user know they need to quit their
135  * editor before the application will come back to life.
136  */
137  {
138  int length;
139  struct bu_vls str = BU_VLS_INIT_ZERO;
140  struct bu_vls sep = BU_VLS_INIT_ZERO;
141  char *editor_basename;
142 
143  if (terminal && editor_opt) {
144  bu_log("Invoking [%s %s %s] via %s\n\n", editor, editor_opt, file, terminal);
145  } else if (terminal) {
146  bu_log("Invoking [%s %s] via %s\n\n", editor, file, terminal);
147  } else if (editor_opt) {
148  bu_log("Invoking [%s %s %s]\n\n", editor, editor_opt, file);
149  } else {
150  bu_log("Invoking [%s %s]\n\n", editor, file);
151  }
152  editor_basename = (char *)bu_calloc(strlen(editor), sizeof(char), "_ged_editit editor_basename");
153  bu_basename(editor_basename, editor);
154  bu_vls_sprintf(&str, "\nNOTE: You must QUIT %s before %s will respond and continue.\n", editor_basename, bu_getprogname());
155  for (length = bu_vls_strlen(&str) - 2; length > 0; length--) {
156  bu_vls_putc(&sep, '*');
157  }
158  bu_log("%s%s%s\n\n", bu_vls_addr(&sep), bu_vls_addr(&str), bu_vls_addr(&sep));
159  bu_vls_free(&str);
160  bu_vls_free(&sep);
161  bu_free(editor_basename, "editor_basename free");
162  }
163 
164 #if defined(SIGINT) && defined(SIGQUIT)
165  s2 = signal(SIGINT, SIG_IGN);
166  s3 = signal(SIGQUIT, SIG_IGN);
167 #endif
168 
169 #ifdef HAVE_UNISTD_H
170  if ((pid = fork()) < 0) {
171  perror("fork");
172  return 0;
173  }
174 #endif
175 
176  if (pid == 0) {
177  /* Don't call bu_log() here in the child! */
178 
179 #if defined(SIGINT) && defined(SIGQUIT)
180  /* deja vu */
181  (void)signal(SIGINT, SIG_DFL);
182  (void)signal(SIGQUIT, SIG_DFL);
183 #endif
184 
185  {
186 
187 #if defined(_WIN32) && !defined(__CYGWIN__)
188  char buffer[RT_MAXLINE + 1] = {0};
189  STARTUPINFO si = {0};
190  PROCESS_INFORMATION pi = {0};
191  si.cb = sizeof(STARTUPINFO);
192  si.lpReserved = NULL;
193  si.lpReserved2 = NULL;
194  si.cbReserved2 = 0;
195  si.lpDesktop = NULL;
196  si.dwFlags = 0;
197 
198  snprintf(buffer, RT_MAXLINE, "%s %s", editor, file);
199 
200  CreateProcess(NULL, buffer, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
201  WaitForSingleObject(pi.hProcess, INFINITE);
202  return 1;
203 #else
204  char *editor_basename;
205  editor_basename = (char *)bu_calloc(strlen(editor), sizeof(char), "_ged_editit editor_basename");
206  bu_basename(editor_basename, editor);
207  if (BU_STR_EQUAL(editor_basename, "TextEdit")) {
208  /* close stdout/stderr so we don't get blather from TextEdit about service registration failure */
209  close(fileno(stdout));
210  close(fileno(stderr));
211  }
212  bu_free(editor_basename, "editor_basename free");
213 
214  if (!terminal && !editor_opt) {
215  (void)execlp(editor, editor, file, NULL);
216  } else if (!terminal) {
217  (void)execlp(editor, editor, editor_opt, file, NULL);
218  } else if (terminal && !terminal_opt) {
219  (void)execlp(terminal, terminal, editor, file, NULL);
220  } else if (terminal && !editor_opt) {
221  (void)execlp(terminal, terminal, terminal_opt, editor, file, NULL);
222  } else {
223  (void)execlp(terminal, terminal, terminal_opt, editor, editor_opt, file, NULL);
224  }
225 #endif
226  /* should not reach */
227  perror(editor);
228  bu_exit(1, NULL);
229  }
230  }
231 
232 #ifdef HAVE_UNISTD_H
233  /* wait for the editor to terminate */
234  while ((xpid = wait(&status)) >= 0) {
235  if (xpid == pid) {
236  break;
237  }
238  }
239 #endif
240 
241 #if defined(SIGINT) && defined(SIGQUIT)
242  (void)signal(SIGINT, s2);
243  (void)signal(SIGQUIT, s3);
244 #endif
245 
246  if (editstring != NULL)
247  bu_free((void *)avtmp, "ged_editit: avtmp");
248 
249  return 1;
250 }
251 
252 
253 int
254 ged_editit(struct ged *gedp, int argc, const char *argv[])
255 {
256  int ret = 0;
258  GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
259 
260  /* FIXME: this should NOT assume that argv[2] and argv[4] are the
261  * edit string and temp file. should use bu_getopt().
262  */
263  if (argc != 5) {
264  bu_vls_printf(gedp->ged_result_str, "Internal Error: \"%s -e editstring -f tmpfile\" is malformed (argc == %d)", argv[0], argc);
265  return TCL_ERROR;
266  } else {
267  char *edstr = bu_strdup((char *)argv[2]);
268  ret = _ged_editit(edstr, argv[4]);
269  bu_free(edstr, "free tmp editstring copy");
270  return ret;
271  }
272 }
273 
274 
275 /*
276  * Local Variables:
277  * tab-width: 8
278  * mode: C
279  * indent-tabs-mode: t
280  * c-file-style: "stroustrup"
281  * End:
282  * ex: shiftwidth=4 tabstop=8
283  */
char filename[MAXLENGTH]
Definition: human.c:105
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
#define VI_EDITOR
Definition: editit.c:46
Definition: ged.h:338
#define RT_MAXLINE
Definition: raytrace.h:1255
#define GED_CHECK_ARGC_GT_0(_gedp, _argc, _flags)
Definition: ged.h:202
Header file for the BRL-CAD common definitions.
#define WIN_EDITOR
Definition: editit.c:41
#define GED_ERROR
Definition: ged.h:61
int ged_editit(struct ged *gedp, int argc, const char *argv[])
Definition: editit.c:254
#define NANO_EDITOR
Definition: editit.c:44
void bu_vls_free(struct bu_vls *vp)
Definition: vls.c:248
#define GED_CHECK_DATABASE_OPEN(_gedp, _flags)
Definition: ged.h:114
void bu_exit(int status, const char *fmt,...) _BU_ATTR_NORETURN _BU_ATTR_PRINTF23
Definition: bomb.c:195
void * bu_calloc(size_t nelem, size_t elsize, const char *str)
Definition: malloc.c:321
void bu_vls_sprintf(struct bu_vls *vls, const char *fmt,...) _BU_ATTR_PRINTF23
Definition: vls.c:707
const char * bu_which(const char *cmd)
Definition: which.c:43
const char * bu_getprogname(void)
Definition: progname.c:96
size_t bu_vls_strlen(const struct bu_vls *vp)
Definition: vls.c:189
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
struct bu_vls * ged_result_str
Definition: ged.h:357
void bu_basename(char *basename, const char *path)
Definition: basename.c:30
#define MAC_EDITOR
Definition: editit.c:42
#define VIM_EDITOR
Definition: editit.c:45
int _ged_editit(char *editstring, const char *filename)
Definition: editit.c:49
void bu_vls_printf(struct bu_vls *vls, const char *fmt,...) _BU_ATTR_PRINTF23
Definition: vls.c:694
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
#define EMACS_EDITOR
Definition: editit.c:43
size_t bu_argv_from_string(char *argv[], size_t lim, char *lp)
Definition: argv.c:32
#define BU_VLS_INIT_ZERO
Definition: vls.h:84
Definition: vls.h:56
void bu_vls_putc(struct bu_vls *vp, int c)
Definition: vls.c:666
int bu_file_exists(const char *path, int *fd)
Definition: file.c:57
#define TRUE
#define bu_strdup(s)
Definition: str.h:71
#define BU_STR_EQUAL(s1, s2)
Definition: str.h:126