main.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. #include "builddate.h"
  11. #include "config.h"
  12. // windows.h for win machines
  13. #if defined(_WIN32) && !defined(__CYGWIN__)
  14. #include <windows.h>
  15. #include <windowsx.h>
  16. #endif
  17. #include "frmobjectui.h"
  18. #include "gltemplate.h"
  19. #include "gui.h"
  20. #include "gvprpipe.h"
  21. #include "menucallbacks.h"
  22. #include "support.h"
  23. #include "viewport.h"
  24. #include <glade/glade.h>
  25. #include <gtk/gtk.h>
  26. #include <gtk/gtkgl.h>
  27. #ifdef ENABLE_NLS
  28. #include "libintl.h"
  29. #endif
  30. #include "glexpose.h"
  31. #include "glutrender.h"
  32. #include <assert.h>
  33. #include <getopt.h>
  34. #include <stdbool.h>
  35. #include <stdint.h>
  36. #include <stdlib.h>
  37. #include <string.h>
  38. #include <util/alloc.h>
  39. #include <util/exit.h>
  40. #ifdef __APPLE__
  41. #include <mach-o/dyld.h>
  42. #endif
  43. #ifdef __FreeBSD__
  44. #include <sys/sysctl.h>
  45. #include <sys/types.h>
  46. #endif
  47. #if !defined(_WIN32)
  48. #include <unistd.h>
  49. #endif
  50. static char *smyrnaDir; /* path to directory containin smyrna data files */
  51. static char *smyrnaGlade;
  52. /* smyrnaPath:
  53. * Construct pathname for smyrna data file.
  54. * Base file name is given as suffix.
  55. * The function resolves the directory containing the data files,
  56. * and constructs a complete pathname.
  57. * The returned string is malloced, so the application should free
  58. * it later.
  59. * Returns NULL on error.
  60. */
  61. char *smyrnaPath(char *suffix) {
  62. static size_t baselen;
  63. #ifdef _WIN32
  64. char *pathSep = "\\";
  65. #else
  66. char *pathSep = "/";
  67. #endif
  68. assert(smyrnaDir);
  69. if (baselen == 0) {
  70. baselen = strlen(smyrnaDir) + 2;
  71. }
  72. size_t len = baselen + strlen(suffix);
  73. char *buf = gv_calloc(len, sizeof(char));
  74. snprintf(buf, len, "%s%s%s", smyrnaDir, pathSep, suffix);
  75. return buf;
  76. }
  77. static char *useString = "Usage: smyrna [-v?] <file>\n\
  78. -f<WxH:bits@rate> - full-screen mode\n\
  79. -e - draw edges as splines if available\n\
  80. -v - verbose\n\
  81. -? - print usage\n";
  82. static void usage(int v) {
  83. fputs(useString, stdout);
  84. graphviz_exit(v);
  85. }
  86. static char *Info[] = {
  87. "smyrna", /* Program */
  88. PACKAGE_VERSION, /* Version */
  89. BUILDDATE /* Build Date */
  90. };
  91. static char *parseArgs(int argc, char *argv[], ViewInfo *viewinfo) {
  92. int c;
  93. while ((c = getopt(argc, argv, ":eKf:txvV?")) != -1) {
  94. switch (c) {
  95. case 'e':
  96. viewinfo->drawSplines = 1;
  97. break;
  98. case 'v': // FIXME: deprecate and remove -v in future
  99. break;
  100. case 'f':
  101. viewinfo->guiMode = GUI_FULLSCREEN;
  102. viewinfo->optArg = optarg;
  103. break;
  104. case 'V':
  105. fprintf(stderr, "%s version %s (%s)\n", Info[0], Info[1], Info[2]);
  106. graphviz_exit(0);
  107. break;
  108. case '?':
  109. if (optopt == '\0' || optopt == '?')
  110. usage(0);
  111. else {
  112. fprintf(stderr, "smyrna: option -%c unrecognized\n", optopt);
  113. usage(1);
  114. }
  115. break;
  116. }
  117. }
  118. if (optind < argc)
  119. return argv[optind];
  120. else
  121. return NULL;
  122. }
  123. static void windowedMode(int argc, char *argv[]) {
  124. GdkGLConfig *glconfig;
  125. /*combo box to show loaded graphs */
  126. GtkComboBox *graphComboBox;
  127. gtk_set_locale();
  128. gtk_init(&argc, &argv);
  129. if (!(smyrnaGlade))
  130. smyrnaGlade = smyrnaPath("smyrna.glade");
  131. xml = glade_xml_new(smyrnaGlade, NULL, NULL);
  132. GtkWidget *gladewidget = glade_xml_get_widget(xml, "frmMain");
  133. gtk_widget_show(gladewidget);
  134. g_signal_connect(gladewidget, "destroy", G_CALLBACK(mQuitSlot), NULL);
  135. glade_xml_signal_autoconnect(xml);
  136. gtk_gl_init(0, 0);
  137. /* Configure OpenGL framebuffer. */
  138. glconfig = configure_gl();
  139. gladewidget = glade_xml_get_widget(xml, "hbox11");
  140. gtk_widget_hide(glade_xml_get_widget(xml, "vbox13"));
  141. gtk_window_set_deletable(
  142. (GtkWindow *)glade_xml_get_widget(xml, "dlgSettings"), 0);
  143. gtk_window_set_deletable((GtkWindow *)glade_xml_get_widget(xml, "frmTVNodes"),
  144. 0);
  145. create_window(glconfig, gladewidget);
  146. change_cursor(GDK_TOP_LEFT_ARROW);
  147. #ifndef _WIN32
  148. glutInit(&argc, argv);
  149. #endif
  150. gladewidget = glade_xml_get_widget(xml, "hbox13");
  151. graphComboBox = (GtkComboBox *)gtk_combo_box_new_text();
  152. gtk_box_pack_end((GtkBox *)gladewidget, (GtkWidget *)graphComboBox, 1, 1, 10);
  153. gtk_widget_show((GtkWidget *)graphComboBox);
  154. view->graphComboBox = graphComboBox;
  155. if (view->guiMode != GUI_FULLSCREEN)
  156. gtk_main();
  157. }
  158. #if !defined(__APPLE__) && !defined(_WIN32)
  159. /// `readlink`-alike but dynamically allocates
  160. static char *readln(const char *path) {
  161. char *resolved = NULL;
  162. size_t size = 0;
  163. while (true) {
  164. // expand target buffer
  165. resolved = gv_realloc(resolved, size, size == 0 ? 1024 : (size * 2));
  166. size = size == 0 ? 1024 : (size * 2);
  167. // attempt to resolve
  168. {
  169. ssize_t written = readlink(path, resolved, size);
  170. if (written < 0) {
  171. break;
  172. }
  173. if ((size_t)written < size) {
  174. // success
  175. resolved[written] = '\0';
  176. return resolved;
  177. }
  178. }
  179. }
  180. // failed
  181. free(resolved);
  182. return NULL;
  183. }
  184. #endif
  185. /// find an absolute path to the current executable
  186. static char *find_me(void) {
  187. // macOS
  188. #ifdef __APPLE__
  189. {
  190. // determine how many bytes we will need to allocate
  191. uint32_t buf_size = 0;
  192. int rc = _NSGetExecutablePath(NULL, &buf_size);
  193. assert(rc != 0);
  194. assert(buf_size > 0);
  195. char *path = gv_alloc(buf_size);
  196. // retrieve the actual path
  197. if (_NSGetExecutablePath(path, &buf_size) < 0) {
  198. fprintf(stderr, "failed to get path for executable.\n");
  199. graphviz_exit(EXIT_FAILURE);
  200. }
  201. // try to resolve any levels of symlinks if possible
  202. while (true) {
  203. char *buf = readln(path);
  204. if (buf == NULL)
  205. return path;
  206. free(path);
  207. path = buf;
  208. }
  209. }
  210. #elif defined(_WIN32)
  211. {
  212. char *path = NULL;
  213. size_t path_size = 0;
  214. int rc = 0;
  215. do {
  216. size_t size = path_size == 0 ? 1024 : (path_size * 2);
  217. path = gv_realloc(path, path_size, size);
  218. path_size = size;
  219. rc = GetModuleFileName(NULL, path, path_size);
  220. if (rc == 0) {
  221. fprintf(stderr, "failed to get path for executable.\n");
  222. graphviz_exit(EXIT_FAILURE);
  223. }
  224. } while (rc == path_size);
  225. return path;
  226. }
  227. #else
  228. // Linux
  229. char *path = readln("/proc/self/exe");
  230. if (path != NULL)
  231. return path;
  232. // DragonFly BSD, FreeBSD
  233. path = readln("/proc/curproc/file");
  234. if (path != NULL)
  235. return path;
  236. // NetBSD
  237. path = readln("/proc/curproc/exe");
  238. if (path != NULL)
  239. return path;
  240. // /proc-less FreeBSD
  241. #ifdef __FreeBSD__
  242. {
  243. int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
  244. static const size_t MIB_LENGTH = sizeof(mib) / sizeof(mib[0]);
  245. do {
  246. // determine how long the path is
  247. size_t buf_size = 0;
  248. if (sysctl(mib, MIB_LENGTH, NULL, &buf_size, NULL, 0) < 0) {
  249. break;
  250. }
  251. assert(buf_size > 0);
  252. // make enough space for the target path
  253. char *buf = gv_alloc(buf_size);
  254. // resolve it
  255. if (sysctl(mib, MIB_LENGTH, buf, &buf_size, NULL, 0) == 0) {
  256. return buf;
  257. }
  258. free(buf);
  259. } while (0);
  260. }
  261. #endif
  262. #endif
  263. fprintf(stderr, "failed to get path for executable.\n");
  264. graphviz_exit(EXIT_FAILURE);
  265. }
  266. /// find an absolute path to where Smyrna auxiliary files are stored
  267. static char *find_share(void) {
  268. #ifdef _WIN32
  269. const char PATH_SEPARATOR = '\\';
  270. #else
  271. const char PATH_SEPARATOR = '/';
  272. #endif
  273. // find the path to the `smyrna` binary
  274. char *smyrna_exe = find_me();
  275. // assume it is of the form …/bin/smyrna[.exe] and construct
  276. // …/share/graphviz/smyrna
  277. char *slash = strrchr(smyrna_exe, PATH_SEPARATOR);
  278. if (slash == NULL) {
  279. fprintf(stderr, "no path separator in path to self, %s\n", smyrna_exe);
  280. free(smyrna_exe);
  281. graphviz_exit(EXIT_FAILURE);
  282. }
  283. *slash = '\0';
  284. slash = strrchr(smyrna_exe, PATH_SEPARATOR);
  285. if (slash == NULL) {
  286. fprintf(stderr, "no path separator in directory containing self, %s\n",
  287. smyrna_exe);
  288. free(smyrna_exe);
  289. graphviz_exit(EXIT_FAILURE);
  290. }
  291. *slash = '\0';
  292. size_t share_len = strlen(smyrna_exe) + strlen("/share/graphviz/smyrna") + 1;
  293. char *share = gv_alloc(share_len);
  294. snprintf(share, share_len, "%s%cshare%cgraphviz%csmyrna", smyrna_exe,
  295. PATH_SEPARATOR, PATH_SEPARATOR, PATH_SEPARATOR);
  296. free(smyrna_exe);
  297. return share;
  298. }
  299. int main(int argc, char *argv[]) {
  300. smyrnaDir = getenv("SMYRNA_PATH");
  301. if (!smyrnaDir) {
  302. smyrnaDir = find_share();
  303. }
  304. char *package_locale_dir;
  305. #ifdef G_OS_WIN32
  306. {
  307. char *package_prefix =
  308. g_win32_get_package_installation_directory(NULL, NULL);
  309. package_locale_dir =
  310. g_build_filename(package_prefix, "share", "locale", NULL);
  311. g_free(package_prefix);
  312. }
  313. #else
  314. package_locale_dir = g_build_filename(smyrnaDir, "locale", NULL);
  315. #endif /* # */
  316. #ifdef ENABLE_NLS
  317. bindtextdomain(GETTEXT_PACKAGE, package_locale_dir);
  318. bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
  319. textdomain(GETTEXT_PACKAGE);
  320. #endif
  321. view = gv_alloc(sizeof(ViewInfo));
  322. init_viewport(view);
  323. view->initFileName = parseArgs(argc, argv, view);
  324. if (view->initFileName)
  325. view->initFile = 1;
  326. if (view->guiMode == GUI_FULLSCREEN)
  327. cb_glutinit(800, 600, &argc, argv, view->optArg);
  328. else
  329. windowedMode(argc, argv);
  330. g_free(package_locale_dir);
  331. graphviz_exit(0);
  332. }
  333. /**
  334. * @dir .
  335. * @brief interactive graph viewer
  336. */