main.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. /***************************************************************************
  2. * main.c
  3. *
  4. * Mon Apr 18 15:19:04 2005
  5. * Copyright 2005 Benjamin Ducke
  6. ****************************************************************************/
  7. /*
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Library General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. /* TODO:
  23. for 1.04:
  24. EASY STUFF:
  25. - tried to install GeneralStatistics w/o first installing RasterTools or anything else: attempts to install, fails because
  26. of missing raster tools headers! Even though line 379 in reg_deps.c should check for this case !!!
  27. - include $GISBASE/lib in linker path for compilation of extensions [NOT FIXED: see lines preceeding INSTALL action case: 693
  28. -L is added to command line, but problems still exist]
  29. MAYBE EXPORT LD_LIBRARY_PATH ??? (only add $GISBASE/lib if it does not already exist)
  30. - version number in HTML documentation index seems to not get updated when installing a newer version of an extension
  31. - remember to update version number in globals.h
  32. for 1.2 (GRASS 6.4 ?):
  33. - make GRASS store its ./configure command line options in a file in the GISBASE/etc directory, so that
  34. it will be possible for GEM to automatically configure extensions according to the system setup
  35. [this means that configure file needs to be kept in sync with GEM; --configure option can be used
  36. to overwrite this behaviour]
  37. - configure script should not fail but disable options and create config.msgs the
  38. contents of this should be displayed and deleted afterwards
  39. - check if it works with this Mac version of GRASS, as well: http://openosx.com/grass/
  40. - before release: any problems expected with native Win32-Kompilation?:
  41. - location for tmp-files?
  42. - a simple wrapper g.install with GRASS style parameters. Should start an xterm and ask for su pwd,
  43. if necessary
  44. - action to check dependencies of installed extensions
  45. - instead of aborting on each failed dependency: build list of all failed
  46. dependencies before aborting
  47. - mechanism to recursively download and install missing deps from a
  48. provided list of URLs
  49. - instead of calling external tar, switch to tarlib and zlib
  50. - action --validate for checking an extension archive/dir
  51. - add link to HTML help index into each extension's submenu in GIS Manager
  52. - g.install with a real custom-made GUI
  53. [VARIABLE GUI must be set appropriately in run_post() for post script]
  54. this should be an independent Tcl/Tk GUI: install/uninstall/query extensions
  55. - extension database on the internet that can be queried and used with either gem
  56. or g.install
  57. - entries for QGIS
  58. - simplified scheme for installing just a single module
  59. - new scheme for registering menu entries in GIS Manager with stable hooks in menu.tcl
  60. NEED HELP
  61. - GRASS' make install installs all files BENEATH top level dir with UID
  62. set to benni !!! Is this intentional?
  63. - provide gem7x as link in /usr/local/bin as part of grass 7.x base install
  64. - description.html should not contain </body> or </html> as Rules.Make seems
  65. to append those ?
  66. - source install copies files COPYING README REQUIREMENTS.html to somewhere (but where?)
  67. -> into <extension>/src but where else ?
  68. - menu.tcl in GRASS 6.0.0 has "all options 1", but 6.1 will have "all options $tmenu".
  69. How to cater for such things? Maybe in the future there should always be an
  70. Xtns menu provided by GIS Manager and always delimited by reliable tags
  71. - d.m window is a bit to small horizontally
  72. CAVEATS:
  73. - does not allow installation of different versions of the same extension within
  74. the same GRASS bin tree
  75. - does not have a real upgrading mechanism: option --upgrade has been disabled for now
  76. - restore cannot fix partially corrupted entries in HTML and TCL files
  77. - Links to other extension's modules will only work if those extensions are installed
  78. DOCS:
  79. - skeleton contains GPL as default license. Creators of new extensions
  80. need to be aware of this!
  81. - menu.tcl will be backed up as menu.tcl.gem.bak
  82. - HTML docs: users must prefix references to GRASS modules outside the Extension with
  83. "../../html/" !!!
  84. - description files: things inside "<" and ">" will be filtered out as HTML tags, even
  85. if they are none!
  86. - make clear that there are some files which will be rendered both as text and HTML
  87. and therefore need things like <br> while others DO NOT (which ones?)!
  88. - configure script should not fail but disable options and create 'config.msgs'
  89. - deletes config.msg after display
  90. - State clearly that it is recommended to uninstall an older
  91. extension version before installing a new one. STATE CLEARLY that --upgrade is rather
  92. another version of --force than a real updating mechanism (RENAME to --newer)
  93. - document all env vars that gem sets
  94. - STATE CLEARLY, that users should not meddle with stuff in index.html, menu.tcl,
  95. gem-entries/ and docs/extensions/ !!!
  96. - provide a unix man page, ASCII and HTML documentation (write HTML and convert)
  97. - files in the skeleton should always be present, even if they do not containing
  98. information
  99. - make it clear, that uninstall and postinstall are run with su privileges! thus,
  100. author must make sure, that only objects in the GRASS install tree are affected
  101. - user may specify either file name or extension name for uninstall action
  102. - user may list installed extensions by using -q w/o filename. Explain
  103. what is meant by type (e.g. 'src')
  104. - uninstallation works only by extension name, NOT file name !
  105. - compiling extensions on a GRASS install with all but the most basic options
  106. disabled should give very portable binaries! (statically linked binaries?)
  107. BUGS:
  108. - remove cva, install again: WARNING: list item 'cva' exists in index.html (?)
  109. - superfluous warning upon uninstall of extension w/o "entries-gisman"
  110. - due to bad command line parsing, listing installed extensions only works like this:
  111. ./gem70 --grass=/usr/local/grass-7.0.svn -q
  112. NOT like this:
  113. ./gem70 -q --grass=/usr/local/grass-7.0.svn
  114. */
  115. #include <getopt.h>
  116. #include <fcntl.h>
  117. #define LOCAL
  118. #include "globals.h"
  119. void show_help(void)
  120. {
  121. fprintf(stdout, "Usage: gem70 [OPTION] [ACTION] [FILE|DIR]\n");
  122. fprintf(stdout, "Install a GRASS extension from FILE or DIR.\n");
  123. fprintf(stdout, "Manage (installed) GRASS extension(s).\n");
  124. fprintf(stdout, "\nPossible ACTIONs are:\n");
  125. fprintf(stdout, " -i, --install=EXT\tinstall a GRASS extension\n");
  126. fprintf(stdout,
  127. " -u, --uninstall=EXT\tremove an extension from GRASS\n");
  128. fprintf(stdout,
  129. " -q, --query=EXT\tdisplay information about extension/list installed\n");
  130. fprintf(stdout,
  131. " -d, --details=EXT\tdisplay additional details about an extension\n");
  132. fprintf(stdout,
  133. " -c, --clean=EXT\tclean extension's source code directories\n");
  134. fprintf(stdout,
  135. " -t, --test=EXT\tconfigure and compile extension, but don't install\n");
  136. fprintf(stdout,
  137. " -l, --license=EXT\tshow copyright information for an extension\n");
  138. fprintf(stdout,
  139. " -r, --restore\t\trecreate HTML links and GIS Manager entries\n");
  140. fprintf(stdout, " -h, --help\t\tdisplay this help and exit\n");
  141. fprintf(stdout,
  142. " -V, --version\t\toutput version information and exit\n\n");
  143. fprintf(stdout, "\nPossible OPTIONs are:\n");
  144. fprintf(stdout, " -g, --grass=PATH\tpath to GRASS installation dir\n");
  145. fprintf(stdout,
  146. " -b, --binary=NAME\tno compilation: use binary files for system NAME\n");
  147. fprintf(stdout,
  148. " -f, --force\t\tforce action, regardless of dependencies\n");
  149. fprintf(stdout,
  150. " -v, --verbose\t\tdisplay detailed status information\n");
  151. fprintf(stdout, " -s, --skip-config\tskip configure script\n");
  152. fprintf(stdout,
  153. " -x, --config-opts=OPTS\tpass OPTS to configure script\n");
  154. fprintf(stdout,
  155. " -o, --options=OPTS\toptions to pass to the C compiler/linker\n");
  156. fprintf(stdout,
  157. " -C, --config-cmd=CMD\tDefine custom 'configure' command (default=configure)\n");
  158. fprintf(stdout,
  159. " -m, --make-cmd=CMD\tDefine custom 'make' command (default=make)\n");
  160. fprintf(stdout,
  161. "\nWhen run from within a GRASS session, locations of libs, header files\n");
  162. fprintf(stdout,
  163. "and installation target dir will be assumed to match those of the active\n");
  164. fprintf(stdout, "GRASS version. ");
  165. fprintf(stdout,
  166. "Option -g can be used to override these or install extensions\nfrom outside");
  167. fprintf(stdout, "of a GRASS session.\n");
  168. fprintf(stdout,
  169. "Per default, extensions will be compiled from source and then installed.\n");
  170. fprintf(stdout,
  171. "If the exension package contains binaries for the user's platform, they can\n");
  172. fprintf(stdout, "be installed instead using the -b option. ");
  173. fprintf(stdout,
  174. "For installation from source code, a C compiler and make tools are needed.\n");
  175. fprintf(stdout, "\nExample:\n");
  176. fprintf(stdout,
  177. "\tgem70 -b macosx --grass=/usr/local/grass-7.0.0 -i myExtension\n");
  178. fprintf(stdout,
  179. "Installs the MacOS X binaries for 'myExtension' in /usr/local/grass-7.0.0.\n");
  180. exit(0);
  181. }
  182. void show_details(char *package)
  183. {
  184. int error;
  185. char tmp[MAXSTR];
  186. sprintf(tmp, "%s", basename(package));
  187. error = chdir(tmp);
  188. if (error < 0) {
  189. print_error(ERR_NO_ACCESS_EXT,
  190. "extension '%s' not accessible: (%s)\n", package,
  191. strerror(errno));
  192. }
  193. dump_ascii("info", "Detailed information");
  194. /* run post action script */
  195. system("sh post");
  196. exit(0);
  197. }
  198. void show_license(char *package)
  199. {
  200. int error;
  201. char tmp[MAXSTR];
  202. sprintf(tmp, "%s", basename(package));
  203. error = chdir(tmp);
  204. if (error < 0) {
  205. print_error(ERR_NO_ACCESS_EXT,
  206. "extension '%s' not accessible: (%s)\n", package,
  207. strerror(errno));
  208. }
  209. dump_ascii("license", "Detailed information");
  210. /* run post action script */
  211. system("sh post");
  212. exit(0);
  213. }
  214. void show_version(void)
  215. {
  216. fprintf(stdout, "gem70 (GRASS extensions manager) %.2f\n", PROGVERSION);
  217. fprintf(stdout, "Written by Benjamin Ducke\n");
  218. fprintf(stdout, "\nCopyright (C) 2005 Benjamin Ducke\n");
  219. fprintf(stdout,
  220. "This is free software; see the source for copying conditions. There is NO\n");
  221. fprintf(stdout,
  222. "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
  223. exit(0);
  224. }
  225. /* determine options to pass to extension's configure script */
  226. /* TODO: check, if system configuration meets a set of requirements */
  227. /* THIS FUNCTION IS CURRENTLY NOT USED */
  228. void get_configure_options(char *gisbase)
  229. {
  230. FILE *fp;
  231. char str[MAXSTR];
  232. if (strcmp(CONFIG_OPTS, "")) {
  233. /* if user has specified config options on the GEM command line: override anything else */
  234. return;
  235. }
  236. /* check if GISBASE/etc/config.system exists and if so, read options from it */
  237. sprintf(str, "%s/etc/config.system", gisbase);
  238. fp = fopen(str, "r");
  239. if (fp == NULL) {
  240. print_warning
  241. ("could not open %s for read access. Using default configure options.\n",
  242. str);
  243. return;
  244. }
  245. /* config.system may also contain nothing, only comments and/or whitespace */
  246. if (nc_fgets_nb(str, MAXSTR, fp) != NULL) {
  247. strcpy(CONFIG_OPTS, str);
  248. }
  249. }
  250. int main(int argc, char *argv[])
  251. {
  252. char *gisbase;
  253. char *grass_version;
  254. char *grass_major;
  255. char *grass_minor;
  256. char *grass_revision;
  257. char *tmp;
  258. char *url;
  259. char *filepart;
  260. char version_file[MAXSTR];
  261. char package[MAXSTR];
  262. char orgname[MAXSTR];
  263. char pkg_name[MAXSTR];
  264. char *bins;
  265. int pkg_major, pkg_minor, pkg_revision;
  266. FILE *f;
  267. int fd;
  268. char pkg_short_name[MAXSTR];
  269. char invocation[MAXSTR];
  270. char coptions[MAXSTR];
  271. int major, minor, revision;
  272. int option;
  273. int action;
  274. int valid;
  275. struct stat buf;
  276. struct stat buf2;
  277. int error;
  278. int is_directory = 0;
  279. DIR *dir;
  280. struct dirent *dir_entry;
  281. int dir_found;
  282. int option_index = 0;
  283. static struct option long_options[] = {
  284. {"install", 1, NULL, 'i'},
  285. {"uninstall", 1, NULL, 'u'},
  286. {"query", 2, NULL, 'q'},
  287. {"details", 1, NULL, 'd'},
  288. {"clean", 1, NULL, 'c'},
  289. {"test", 1, NULL, 't'},
  290. {"license", 1, NULL, 'l'},
  291. {"restore", 0, NULL, 'r'},
  292. {"help", 0, NULL, 'h'},
  293. {"version", 0, NULL, 'V'},
  294. {"grass", 1, NULL, 'g'},
  295. {"options", 1, NULL, 'o'},
  296. {"binary", 1, NULL, 'b'},
  297. {"force", 0, NULL, 'f'},
  298. {"verbose", 0, NULL, 'v'},
  299. {"skip-config", 0, NULL, 's'},
  300. {"config-opts", 1, NULL, 'x'},
  301. {"config-cmd", 1, NULL, 'C'},
  302. {"make-cmd", 1, NULL, 'm'},
  303. {0, 0, 0, 0}
  304. };
  305. /* set global variables to defaults */
  306. VERBOSE = 0;
  307. TMPCLEAN = 0;
  308. TMPDBCLEAN = 0;
  309. FORCE = 0;
  310. UPGRADE = 0;
  311. ERROR = 0;
  312. WARNINGS = 0;
  313. SKIP_CFG = 0;
  314. strcpy(GISMAN_CMD, "");
  315. strcpy(GISMAN2_CMD, "");
  316. strcpy(QGIS_CMD, "");
  317. strcpy(UNINSTALL_CMD, "");
  318. strcpy(HTML_CMD, "");
  319. strcpy(TMPDIR, "");
  320. strcpy(TMPDB, "");
  321. strcpy(TMP_GISMAN, "");
  322. strcpy(TMP_DESCR, "");
  323. strcpy(TMP_INFO, "");
  324. strcpy(TMP_DEPS, "");
  325. strcpy(TMP_BUGS, "");
  326. strcpy(TMP_AUTHORS, "");
  327. strcpy(TMP_HTML, "");
  328. strcpy(TMP_NULL, "");
  329. strcpy(CONFIG_OPTS, "");
  330. strcpy(CONFIG_CMD, "configure");
  331. strcpy(MAKE_CMD, "make");
  332. getcwd(CWD, MAXSTR);
  333. /* reset terminal colors */
  334. fprintf(stdout, "\033[0m");
  335. tmp = malloc(sizeof(char) * MAXSTR);
  336. strcpy(invocation, argv[0]);
  337. /* all output should be unbuffered */
  338. setvbuf(stdout, (char *)NULL, _IONBF, 0);
  339. /* if run with no arguments: show help */
  340. if (argc == 1) {
  341. /* show usage info and exit */
  342. show_help();
  343. exit(0);
  344. }
  345. atexit(&exit_msg); /* show a message after program termination */
  346. valid = 0;
  347. bins = NULL;
  348. gisbase = NULL;
  349. opterr = 0;
  350. option =
  351. getopt_long(argc, argv, ":i:u:q:d:c:C:t:l:m:o:x:rhVg:b:fvs",
  352. long_options, &option_index);
  353. while (option != -1) {
  354. if (option == '?') {
  355. print_error(ERR_INVOCATION,
  356. "unknown option or action specified.\n");
  357. }
  358. /* check for missing arguments */
  359. if (option == ':') {
  360. if ((optopt == 'i') || (optopt == 'u') || (optopt == 'd') ||
  361. (optopt == 'c') || (optopt == 't') || (optopt == 'l') ||
  362. (optopt == 'r')) {
  363. print_error(ERR_INVOCATION,
  364. "missing file or directory name.\n");
  365. }
  366. if (optopt == 'g') {
  367. print_error(ERR_INVOCATION, "missing path to GRASS 7.\n");
  368. }
  369. if (optopt == 'b') {
  370. print_error(ERR_INVOCATION,
  371. "missing name of binary architecture.\n");
  372. }
  373. if (optopt == 'b') {
  374. print_error(ERR_INVOCATION, "missing configure options.\n");
  375. }
  376. if (optopt == 'q') {
  377. /* '-q' w/o filename is list action */
  378. action = LIST;
  379. valid++;
  380. break;
  381. }
  382. }
  383. if ((option == 'i') || (option == 'u') || (option == 'q') ||
  384. (option == 'd') || (option == 'c') || (option == 't') ||
  385. (option == 'l') || (option == 'r') || (option == 'h') ||
  386. (option == 'V')) {
  387. /* got a valid action specifier */
  388. valid++;
  389. /* set action accordingly */
  390. switch (option) {
  391. case 'i':
  392. if (action != BIN_INSTALL) {
  393. action = INSTALL;
  394. }
  395. break;
  396. case 'u':
  397. action = UNINSTALL;
  398. break;
  399. case 'q':
  400. action = QUERY;
  401. break;
  402. case 'd':
  403. action = DETAILS;
  404. break;
  405. case 'c':
  406. action = CLEAN;
  407. break;
  408. case 't':
  409. action = TEST_INSTALL;
  410. break;
  411. case 'l':
  412. action = LICENSE;
  413. break;
  414. case 'r':
  415. action = RESTORE;
  416. break;
  417. case 'h':
  418. action = HELP;
  419. break;
  420. case 'V':
  421. action = VERSION;
  422. break;
  423. }
  424. if (optarg != NULL) {
  425. /* save package name as given on command line */
  426. strcpy(package, optarg);
  427. /* orgname will always preserve the commandline option */
  428. strcpy(orgname, optarg);
  429. }
  430. }
  431. /* set options */
  432. if (option == 'g') {
  433. gisbase = malloc(sizeof(char) * (strlen(optarg) + 1));
  434. strcpy(gisbase, optarg);
  435. }
  436. if (option == 'b') {
  437. bins = malloc(sizeof(char) * (strlen(optarg) + 1));
  438. strcpy(bins, optarg);
  439. action = BIN_INSTALL;
  440. }
  441. if (option == 'x') {
  442. /* configure script options */
  443. strcpy(&CONFIG_OPTS[0], optarg);
  444. }
  445. if (option == 'f') {
  446. FORCE = 1;
  447. }
  448. if (option == 'v') {
  449. VERBOSE = 1;
  450. }
  451. if (option == 's') {
  452. SKIP_CFG = 1;
  453. }
  454. if (option == 'o') {
  455. /* GEM_C_OPTS gets passed to the C compiler via the GRASS/GEM Makefiles:
  456. <EXT>/src/include/Make/Grass.make.in:
  457. CFLAGS = $(INC) $(COMPILE_FLAGS) $(USE_TERMIO) $(GEM_C_OPTS)
  458. */
  459. strcat(coptions, optarg);
  460. }
  461. /* define a custom configure command */
  462. if (option == 'C') {
  463. strcpy(CONFIG_CMD, optarg);
  464. }
  465. /* define a custom make command */
  466. if (option == 'm') {
  467. strcpy(MAKE_CMD, optarg);
  468. }
  469. /* get next option from command line */
  470. option =
  471. getopt_long(argc, argv, ":i:u:q:d:c:t:l:o:x:rhVg:b:fvs",
  472. long_options, &option_index);
  473. }
  474. if (valid < 1) {
  475. print_error(ERR_INVOCATION, "please specify a valid action.\n");
  476. }
  477. if (valid > 1) {
  478. print_error(ERR_INVOCATION, "please specify only one action.\n");
  479. }
  480. /* export compiler options for use by Makefiles */
  481. sprintf(GEM_C_OPTS, "GEM_C_OPTS=%s", coptions);
  482. putenv(GEM_C_OPTS);
  483. /* these actions can be done without any extension checking */
  484. if (action == HELP) {
  485. /* show usage info and exit */
  486. show_help();
  487. exit(0);
  488. }
  489. if (action == VERSION) {
  490. /* show version info and exit */
  491. show_version();
  492. exit(0);
  493. }
  494. if (!VERBOSE) {
  495. /* set temp file to pipe output to for silent operation */
  496. /* TODO: Do not hardcode temp paths */
  497. strcpy(TMP_NULL, "/tmp/grass.extension.log.XXXXXX"); /* TMP_NULL is a global variable */
  498. mkstemp(TMP_NULL);
  499. fd = open(TMP_NULL, O_CREAT);
  500. if (fd == -1) {
  501. print_error(ERR_TMPFILE, "could not create temp file: %s",
  502. strerror(errno));
  503. exit(ERR_TMPFILE);
  504. }
  505. }
  506. /* these actions need a valid GRASS path but no extensions */
  507. if (action == RESTORE) {
  508. /* figure out path to GRASS installation */
  509. /* GIS base not given? */
  510. if (gisbase == NULL) {
  511. /* try to read from GRASS environment */
  512. gisbase = getenv("GISBASE");
  513. if (gisbase == NULL) {
  514. /* still NULL? Abort! */
  515. print_error(ERR_INVOCATION,
  516. "GISBASE environment variable not set and path to GRASS not given.\n");
  517. }
  518. }
  519. if (VERBOSE) {
  520. fprintf(stdout, "Path to GRASS is %s.\n", gisbase);
  521. }
  522. restore(gisbase, grass_version);
  523. exit(0);
  524. }
  525. if (action == LIST) {
  526. /* figure out path to GRASS installation */
  527. /* GIS base not given? */
  528. if (gisbase == NULL) {
  529. /* try to read from GRASS environment */
  530. gisbase = getenv("GISBASE");
  531. if (gisbase == NULL) {
  532. /* still NULL? Abort! */
  533. print_error(ERR_INVOCATION,
  534. "GISBASE environment variable not set and path to GRASS not given.\n");
  535. }
  536. }
  537. if (VERBOSE) {
  538. fprintf(stdout, "Path to GRASS is %s.\n", gisbase);
  539. }
  540. list_extensions(gisbase);
  541. exit(0);
  542. }
  543. /* check if extension is stored in a remote URL */
  544. if ((strstr(package, "http://")) || (strstr(package, "ftp://"))) {
  545. wget_extension(package); /* download into current dir using wget */
  546. /* cut off the path specification */
  547. url = malloc(sizeof(char) * MAXSTR);
  548. strcpy(url, package);
  549. filepart = strrchr(url, '/');
  550. filepart++;
  551. strcpy(package, filepart);
  552. free(url);
  553. }
  554. if (VERBOSE) {
  555. fprintf(stdout, "Extension location is '%s'.\n", package);
  556. }
  557. if (action != UNINSTALL) {
  558. error = stat(package, &buf);
  559. if (error < 0) {
  560. print_error(ERR_NO_ACCESS_EXT,
  561. "extension FILE or DIR '%s' invalid: %s\n", package,
  562. strerror(errno));
  563. }
  564. if (S_ISDIR(buf.st_mode)) {
  565. is_directory = 1;
  566. if (VERBOSE) {
  567. fprintf(stdout, "Extension files stored in a directory.\n");
  568. }
  569. }
  570. else {
  571. if (VERBOSE) {
  572. fprintf(stdout,
  573. "Extension files stored in a package file.\n");
  574. }
  575. /* DECOMPRESS INTO TEMP DIR, CHANGE NAME OF package TO THAT DIR */
  576. unpack_extension(package);
  577. /* find name of directory containing extension files */
  578. /* very primitive: just picks the first directory */
  579. dir = opendir(TMPDIR);
  580. dir_entry = readdir(dir);
  581. dir_found = 0;
  582. while (dir_entry != NULL) {
  583. if ((strcmp(dir_entry->d_name, ".")) &&
  584. (strcmp(dir_entry->d_name, ".."))
  585. ) {
  586. /* check if it is a directory */
  587. sprintf(tmp, "%s/%s", TMPDIR, dir_entry->d_name);
  588. stat(tmp, &buf2);
  589. if (S_ISDIR(buf2.st_mode)) {
  590. dir_found = 1;
  591. break;
  592. }
  593. }
  594. dir_entry = readdir(dir);
  595. }
  596. strcpy(package, tmp);
  597. if (dir_found == 0) {
  598. print_error(ERR_UNPACK_EXT,
  599. "no top-level directory found in extension package.\n");
  600. }
  601. }
  602. }
  603. /* copy package name into this maliciously named variable */
  604. /* (sorry about the mess ...) */
  605. /* This name will be used for all registration actions and */
  606. /* for creating files and directories that store extension */
  607. /* information for uninstall and restore actions */
  608. /* For uninstall, we take the last argument as package, NOT */
  609. /* file name */
  610. if (action == UNINSTALL) {
  611. strcpy(pkg_short_name, package);
  612. }
  613. else {
  614. get_package_name(package, pkg_short_name);
  615. }
  616. /* export relevant VARS for use by post script */
  617. if (valid > 0) {
  618. /* export all relevant env vars for the post script */
  619. if (gisbase == NULL) {
  620. /* try to read from GRASS environment */
  621. gisbase = getenv("GISBASE");
  622. }
  623. run_post(package, action, bins, gisbase);
  624. }
  625. if (VERBOSE) {
  626. fprintf(stdout, "Extension will be installed from '%s'\n", package);
  627. }
  628. /* CHECK PACKAGE FOR VALIDITY */
  629. if (action != UNINSTALL) {
  630. check_extension(package, pkg_name, &pkg_major, &pkg_minor,
  631. &pkg_revision);
  632. }
  633. /* these actions can be done without GRASS checking */
  634. if (action == QUERY) {
  635. query_extension(package, pkg_name, pkg_major, pkg_minor, pkg_revision,
  636. pkg_short_name, invocation, orgname);
  637. exit(0);
  638. }
  639. if (action == DETAILS) {
  640. show_details(package);
  641. exit(0);
  642. }
  643. if (action == LICENSE) {
  644. show_license(package);
  645. exit(0);
  646. }
  647. if (action == CLEAN) {
  648. source_clean(package);
  649. exit(0);
  650. }
  651. /* The following checks need to be done for all other actions! */
  652. /* figure out path to GRASS installation */
  653. /* GIS base not given? */
  654. if (gisbase == NULL) {
  655. /* try to read from GRASS environment */
  656. gisbase = getenv("GISBASE");
  657. if (gisbase == NULL) {
  658. /* still NULL? Abort! */
  659. print_error(ERR_INVOCATION,
  660. "GISBASE environment variable not set and path to GRASS not given.\n");
  661. }
  662. }
  663. if (VERBOSE) {
  664. fprintf(stdout, "Path to GRASS is %s.\n", gisbase);
  665. }
  666. /* figure out GRASS version */
  667. grass_version = getenv("GRASS_VERSION");
  668. if (grass_version == NULL) {
  669. /* GRASS version can be read from gisbase/etc/VERSIONNUMBER */
  670. sprintf(version_file, "%s/etc/VERSIONNUMBER", gisbase);
  671. f = fopen(version_file, "r");
  672. if (f == NULL) {
  673. /* still NULL? Abort! */
  674. print_error(ERR_VERSION,
  675. "Could not read GRASS version. Did you specify the right path?\n");
  676. }
  677. else {
  678. grass_version = malloc(sizeof(char) * 16);
  679. error = fscanf(f, "%s", grass_version);
  680. fclose(f);
  681. if (error < 1) {
  682. print_error(ERR_VERSION,
  683. "Could not read GRASS version. Did you specify the right path?\n");
  684. }
  685. }
  686. }
  687. if (grass_version != NULL) {
  688. /* extract major and minor version numbers */
  689. tmp = strdup(grass_version);
  690. grass_major = strtok(tmp, ".");
  691. grass_minor = strtok(NULL, ".");
  692. grass_revision = strtok(NULL, ".");
  693. major = strtol(grass_major, NULL, 10);
  694. minor = strtol(grass_minor, NULL, 10);
  695. revision = strtol(grass_revision, NULL, 10);
  696. grass_version = malloc(sizeof(char) * MAXSTR);
  697. sprintf(grass_version, "%i.%i.%i", major, minor, revision);
  698. if (VERBOSE) {
  699. fprintf(stdout, "GRASS version is %s.\n", grass_version);
  700. }
  701. if (major < 6) {
  702. print_error(ERR_VERSION,
  703. "extensions only work with GRASS version 6 and above.\n");
  704. }
  705. }
  706. /* for GDAL compatibility and
  707. for the sake of people using Lorenzo Moretti's GRASS for MacOS, we always export the following
  708. compiler options */
  709. sprintf(coptions, "-L%s/lib -I/usr/local/grasslib/include/ ", gisbase);
  710. /* these actions can only be done after everything has been checked */
  711. if (action == INSTALL) {
  712. source_install(package, gisbase, pkg_short_name, pkg_major, pkg_minor,
  713. pkg_revision, grass_version);
  714. exit(0);
  715. }
  716. if (action == UNINSTALL) {
  717. uninstall(package, pkg_short_name, gisbase, grass_version);
  718. exit(0);
  719. }
  720. if (action == TEST_INSTALL) {
  721. test_install(package, gisbase, pkg_short_name, pkg_major, pkg_minor,
  722. pkg_revision, grass_version);
  723. exit(0);
  724. }
  725. if (action == BIN_INSTALL) {
  726. if (binaries_exist(package, bins)) {
  727. bin_install(package, gisbase, bins, pkg_short_name, pkg_major,
  728. pkg_minor, pkg_revision, grass_version);
  729. exit(0);
  730. }
  731. else {
  732. print_error(ERR_MISSING_BINS, "no binaries for system '%s'\n",
  733. bins);
  734. }
  735. }
  736. exit(0);
  737. }