-
Notifications
You must be signed in to change notification settings - Fork 361
/
gmt_api.c
13220 lines (12204 loc) · 663 KB
/
gmt_api.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*--------------------------------------------------------------------
*
* Copyright (c) 1991-2020 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
* See LICENSE.TXT file for copying and redistribution conditions.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 3 or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* Contact info: www.generic-mapping-tools.org
*--------------------------------------------------------------------*/
/*
* Public functions for the GMT C/C++ API. The API consist of functions
* in gmt_api.c, gmt_parse.c, and all the GMT modules; see gmt.h for list.
*
* Author: Paul Wessel
* Date: 1-JUN-2013
* Version: 5
*
* The API presently consists of 68 documented functions. For a full
* description of the API, see the GMT_API documentation.
* These functions have Fortran bindings as well, provided you add
* -DFORTRAN_API to the C preprocessor flags [in ConfigUserAdvanced.cmake].
*
* There are 2 public functions used for GMT API session handling.
* This part of the API helps the developer create and delete GMT sessions:
*
* GMT_Create_Session : Initialize a new GMT session
* GMT_Destroy_Session : Destroy a GMT session
*
* There is 2 public functions for common error reporting.
* Errors will be reported to stderr or selected log file:
*
* GMT_Message : Report an message given a verbosity level
* GMT_Report : Report an error given an error code
*
* There are 31 further public functions used for GMT i/o activities:
*
* GMT_Alloc_Segment : Allocate a single DATASET segment
* GMT_Begin_IO : Allow i/o to take place for rec-by-rec operations
* GMT_Convert_Data : Convert between different data sets, if possible
* GMT_Create_Data : Return an empty container for a new data set
* GMT_Destroy_Data : Destroy a data set and its container
* GMT_Duplicate_Data : Make an exact duplicate of a dataset
* GMT_Duplicate_String : Allocates a copy of a string to be freed by API
* GMT_End_IO : Disallow further rec-by-rec i/o
* GMT_Get_Info : Get meta-data from the object passed
* GMT_Get_Record : Get the next single data record from the source(s)
* GMT_Get_Row : Read one row from a grid
* GMT_Get_Status : Examine current status of record-by-record i/o
* GMT_Get_Matrix : Get user matrix from GMT_MATRIX array
* GMT_Get_Vector : Get user vector from GMT_VECTOR column
* GMT_Get_Strings : Get user strings from GMT_VECTOR or MATRIX container
* GMT_Init_IO : Initialize rec-by-rec i/o machinery before program use
* GMT_Init_VirtualFile : Reset a virtual file for reuse
* GMT_Inquire_VirtualFile : Determine family of a virtual file
* GMT_Open_VirtualFile : Open a memory location for reading or writing by a module
* GMT_Put_Record : Send the next output record to its destination
* GMT_Put_Row : Write one row to a grid
* GMT_Put_Matrix : Hook user matrix to GMT_MATRIX array
* GMT_Put_Vector : Hook user vector to GMT_VECTOR column
* GMT_Put_Strings : Hook user strings to GMT_VECTOR or MATRIX container
* GMT_Read_Data : Load data into program memory from selected source
* GMT_Read_Group : Read numerous files into an array of objects
* GMT_Read_VirtualFile : Obtain the memory resource that a module wrote to.
* GMT_Register_IO : Register a source (or destination) for i/o use
* GMT_Set_Comment : Update a comment for a data set
* GMT_Write_Data : Place data set from program memory to selected destination
* GMT_Encode_Options : Used by external APIs to fill out options from implicit rules
* The above functions deal with registration of input sources (files,
* streams, file handles, or memory locations) and output destinations
* (same flavors as input), the setup of the i/o, and generic functions
* to access the data either in one go (GMT_Get|Put_Data) or on a
* record-by-record basis (GMT_Get|Put_Record). Finally, data sets that
* are allocated can then be destroyed when no longer needed.
*
* There are 6 functions that deal with options, defaults and arguments:
*
* GMT_Get_Common : Checks for and returns values for GMT common options
* GMT_Get_Default : Return the value of a GMT parameter as a string
* GMT_Get_Enum : Return the integer constant of a GMT API enum.
* GMT_Get_Values : Convert string to one or more coordinates or dimensions
* GMT_Set_Default : Set a GMT parameter via a strings
* GMT_Option : Display syntax for one or more GMT common options
*
* One function handles the listing of modules and the calling of any GMT module:
*
* GMT_Call_Module : Call the specified GMT module
*
* Four functions are used to get grid index from row, col, and to obtain coordinates
*
* GMT_Get_Coord : Return array of coordinates for one dimension
* GMT_Get_Index : Return 1-D grid index given row, col
* GMT_Get_Pixel : Return 1-D image index given row, col, layer
* GMT_Set_Columns : Specify number of output columns for rec-by-rec writing
*
* For FFT operations there are 8 additional API functions:
*
* GMT_FFT : Call the forward or inverse FFT
* GMT_FFT_1D : Lower-level 1-D FFT call
* GMT_FFT_2D : Lower-level 2-D FFT call
* GMT_FFT_Create : Initialize the FFT machinery for given dimension
* GMT_FFT_Destroy : Destroy FFT machinery
* GMT_FFT_Option : Display the syntax of the GMT FFT option settings
* GMT_FFT_Parse : Parse the GMT FFT option
* GMT_FFT_Wavenumber : Return selected wavenumber given its type
*
* There are also 13 functions for argument and option parsing. See gmt_parse.c for these.
*
* Finally, three low-level F77-callable functions for grid i/o are given:
*
* gmt_f77_readgrdinfo_ : Read the header of a GMT grid
* gmt_f77_readgrd_ : Read a GMT grid from file
* gmt_f77_writegrd_ : Write a GMT grid to file
*
* --------------------------------------------------------------------------------------------
* Guru notes on memory management: Paul Wessel, June 2013.
*
* GMT maintains control over allocating, reallocating, and freeing of GMT objects created by GMT.
* Because GMT_modules may be given files, memory locations, streams, etc., as input and output we
* have to impose some structure as how this will work seamlessly. Here, "GMT object" refers to
* any of the 5 GMT resources: grids, images, datasets, palettes, and PostScript.
*
* 1. When GMT allocates memory for a GMT object it sets its alloc_mode to GMT_ALLOC_INTERNALLY (1)
* and its alloc_level to <module level>. This is 0 for the gmt.c UNIX application as well as
* for any external API (MEX, Python, Julia), 1 for any GMT module called, 2 for modules called
* by top-level modules, etc., as far down as the thread goes.
* 2. Memory not allocated by GMT will have an implicit alloc_mode = GMT_ALLOC_EXTERNALLY [0]
* and alloc_level = 0 (i.e., gmt executable or API level) but it does not matter since such memory is
* only used for reading and we may never free it or reallocate it within GMT. This alloc_mode
* only applies to data arrays inside objects (e.g., G->data), not the GMT objects themselves.
* The GMT objects (the "containers") are freed at the end of their level, if not before.
* 3. Memory passed into modules as "input file" requires no special treatment since its level
* will be lower than that of the module it is used in, and when that module tries to free it
* (directly with GMT_Destroy_Data or via end-of-module gmtlib_garbage_collection) it will skip
* it as its level does not match the current module level. A module can only free memory that
* it allocated; the exception is the top-level gmt application.
* 4. Passing memory out of a module (i.e., "writing to memory") requires that the calling function
* first create an output object and use the ID to encode the memory filename (@GMTAPI@-######).
* The object stores the level it was called at. Pass the encoded filename as the output file.
* When GMT_Create_Data is called with no dimensions then the direction is set to GMT_OUT and
* we set the object's messenger flag to true. This is used so that when the dataset we wish to
* return out of a module is built it replaces the empty initial dataset but inherits that dataset's
* alloc_level so it may survive the life of the module process.
* Internally, the memory that the module allocates (e.g., grid, dataset, etc.) will initially
* have an alloc_level matching the module level (and would be freed if written to a regular
* file). However, when GMT_Write_Data is called and we branch into the GMT_REFERENCE case we
* instead take the following steps:
* a) The registered output API->object's resource pointer is set to the GMT object that the
* module allocated (this is how we pass the data out of a module).
* b) The GMT object's alloc_level is changed to equal the output API->object's level (this
* is how it will survive beyond the end of the module).
* c) The API object originally pointing to the GMT object is flagged by having its variable
* no_longer_owner set to true (this is how we avoid freeing something twice).
* When the module ends there are two API objects with references to the GMT object: the internal
* module object and the output object. The first is set to NULL by gmtlib_garbage_collection because
* the object is no longer the owner of the data. The second is ignored because its level is too low.
* After that any empty API objects are removed (so the no_longer_owner one is removed), while
* the second survives the life of the module, as we require.
*
* Thus, at the session (gmt) level all GMT objects have alloc_level = 0 since anything higher will
* have been freed by a module. GMT_Destroy_Session finally calls gmtlib_garbage_collection a final
* time and he frees any remaining GMT objects.
*
* Notes on family vs actual_family:
* The S->actual_family contains the object type that we allocated. However, we allow modules
* that expect a DATASET to instead be passed a GMT_VECTOR or GMT_MATRIX. If so then S->family
* will be GMT_IS_DATASET while the actual_family remains GMT_VECTOR|GMT_MATRIX. The i/o functions
* GMT_Read_Data, GMT_Put_Record, etc knows how to deal with this.
*/
/*!
* \file gmt_api.c
* \brief Public functions for the GMT C/C++ API.
*/
#include "gmt_dev.h"
#include "gmt_internals.h"
#include "gmt_sharedlibs.h" /* Common shared libs structures */
#include <stdarg.h>
#ifdef HAVE_DIRENT_H_
# include <dirent.h>
#endif
#ifdef HAVE_SYS_DIR_H_
# include <sys/dir.h>
#endif
/* Possibly define missing shared library constants */
#ifndef DT_DIR
# define DT_DIR 4
#endif
#ifndef RTLD_LAZY
# define RTLD_LAZY 1
#endif
#ifdef WIN32 /* Special for Windows */
# include <process.h>
# define getpid _getpid
#endif
#define GMTAPI_MAX_ID 999999 /* Largest integer that will fit in the %06d format */
#define GMTAPI_UNLIMITED 0 /* Using 0 to mean we may allow 1 or more data objects of this family */
#ifdef FORTRAN_API
/* Global structure pointer needed for FORTRAN-77 [PW: not tested yet - is it even needed?] */
static struct GMTAPI_CTRL *GMT_FORTRAN = NULL;
#endif
static int GMTAPI_session_counter = 0; /* Keeps track of the ID of new sessions for multi-session programs */
/* Grid node lookup */
static uint64_t (*GMTAPI_index_function) (struct GMT_GRID_HEADER *, uint64_t, uint64_t, uint64_t); /* Pointer to index function (for images only) */
/*! Macros that report error, then return a NULL pointer, the error, or a value, respectively */
#define return_null(API,err) { gmtlib_report_error(API,err); return (NULL);}
#define return_error(API,err) { gmtlib_report_error(API,err); return (err);}
#define return_value(API,err,val) { gmtlib_report_error(API,err); return (val);}
/* We asked for subset of grid if the wesn pointer is not NULL and indicates a nonzero region */
#define full_region(wesn) (!wesn || (wesn[XLO] == wesn[XHI] && wesn[YLO] == wesn[YHI]))
/* DATASET can be given via many individual files. */
#define multiple_files_ok(family) (family == GMT_IS_DATASET)
/* GRID and IMAGE can be read it two steps (header, then data). */
#define a_grid_or_image(family) (family == GMT_IS_GRID || family == GMT_IS_IMAGE)
/* Misc. local text strings needed in this file only, used when debug verbose is on (-Vd).
* NOTE: The order of these MUST MATCH the order in the enums in gmt_resources.h! */
static const char *GMT_method[] = {"File", "Stream", "File Descriptor", "Memory Copy", "Memory Reference"};
static const char *GMT_family[] = {"Data Table", "Grid", "Image", "CPT", "PostScript", "Matrix", "Vector", "Coord"};
static const char *GMT_direction[] = {"Input", "Output"};
static const char *GMT_stream[] = {"Standard", "User-supplied"};
static const char *GMT_status[] = {"Unused", "In-use", "Used"};
static const char *GMT_geometry[] = {"Not Set", "Point", "Line", "Polygon", "Point|Line|Poly", "Line|Poly", "Surface", "Non-Geographical", "Text"};
static const char *GMT_class[] = {"QUIET", "NOTICE", "ERROR", "WARNING", "TIMING", "INFORMATION", "COMPATIBILITY", "DEBUG"};
static unsigned int GMT_no_pad[4] = {0, 0, 0, 0};
static const char *GMT_family_abbrev[] = {"D", "G", "I", "C", "X", "M", "V", "-"};
/*! Two different i/o mode: GMT_Put|Get_Data vs GMT_Put|Get_Record */
enum GMT_enum_iomode {
GMTAPI_BY_SET = 0, /* Default is to read the entire dataset or texset */
GMTAPI_BY_REC = 1}; /* Means we will access the registere files on a record-by-record basis */
/*! Entries into dim[] for matrix or vector */
enum GMT_dim {
GMTAPI_HDR_POS = 3, /* Used with curr_pos to keep track of table headers only */
GMTAPI_DIM_COL = 0, /* Holds the number of columns for vectors and x-nodes for matrix */
GMTAPI_DIM_ROW = 1}; /* Holds the number of rows for vectors and y-nodes for matrix */
enum GMTAPI_enum_input {
GMTAPI_OPTION_INPUT = 0, /* Input resource specified via an option (e.g., -G<file>) */
GMTAPI_MODULE_INPUT = 1}; /* Input resource specified via the module command line */
enum GMTAPI_enum_status {
GMTAPI_GOT_SEGHEADER = -1, /* Read a segment header */
GMTAPI_GOT_SEGGAP = -2}; /* Detected a gap and insertion of new segment header */
/*==================================================================================================
* PRIVATE FUNCTIONS ONLY USED BY THIS LIBRARY FILE
*==================================================================================================
*
* gmtapi_* functions are static and only used in gmt_api.c
* gmtlib_* functions are exported and may be used in other gmt_*.c files
*/
GMT_LOCAL void gmtapi_get_record_init (struct GMTAPI_CTRL *API);
GMT_LOCAL int gmtapi_sort_on_classic (const void *vA, const void *vB) {
const struct GMT_MODULEINFO *A = vA, *B = vB;
if (A == NULL) return +1; /* Get the NULL entry to the end */
if (B == NULL) return -1; /* Get the NULL entry to the end */
return strcmp(A->cname, B->cname);
}
GMT_LOCAL int gmtapi_sort_on_modern (const void *vA, const void *vB) {
const struct GMT_MODULEINFO *A = vA, *B = vB;
if (A == NULL) return +1; /* Get the NULL entry to the end */
if (B == NULL) return -1; /* Get the NULL entry to the end */
return strcmp(A->mname, B->mname);
}
/* Function to exclude some special core modules from being reported by gmt --help|show-modules */
GMT_LOCAL int gmtapi_skip_this_module (const char *name) {
if (!strncmp (name, "gmtread", 7U)) return 1; /* Skip the gmtread module */
if (!strncmp (name, "gmtwrite", 8U)) return 1; /* Skip the gmtwrite module */
return 0; /* Display this one */
}
/* Function to exclude modern mode modules from being reported by gmt --show-classic */
GMT_LOCAL int gmtapi_skip_modern_module (const char *name) {
if (!strncmp (name, "subplot", 7U)) return 1; /* Skip the subplot module */
if (!strncmp (name, "figure", 6U)) return 1; /* Skip the figure module */
if (!strncmp (name, "begin", 5U)) return 1; /* Skip the begin module */
if (!strncmp (name, "clear", 5U)) return 1; /* Skip the clear module */
if (!strncmp (name, "inset", 5U)) return 1; /* Skip the inset module */
if (!strncmp (name, "movie", 5U)) return 1; /* Skip the movie module */
if (!strncmp (name, "docs", 4U)) return 1; /* Skip the docs module */
if (!strncmp (name, "end", 3U)) return 1; /* Skip the end module */
return 0; /* Display this one */
}
/* Pretty print all GMT core module names and their purposes for gmt --help */
void gmtlib_module_show_all (void *V_API, struct GMT_MODULEINFO M[], const char *title) {
unsigned int module_id = 0, n;
char message[GMT_LEN256];
struct GMTAPI_CTRL *API = gmt_get_api_ptr (V_API);
GMT_Message (V_API, GMT_TIME_NONE, "\n=== %s ===\n", title);
while (M[module_id].cname != NULL) {
if (module_id == 0 || strcmp (M[module_id-1].component, M[module_id].component)) {
/* Start of new supplemental group */
snprintf (message, GMT_LEN256, "\nModule name: Purpose of %s module:\n", M[module_id].component);
GMT_Message (V_API, GMT_TIME_NONE, message);
GMT_Message (V_API, GMT_TIME_NONE, "----------------------------------------------------------------\n");
}
n = module_id + 1; /* Determine extent of this component lib */
while (M[n].cname != NULL && !strcmp (M[n-1].component, M[n].component)) n++;
/* Sort array on modern names */
qsort (&M[module_id], n-module_id, sizeof (struct GMT_MODULEINFO), gmtapi_sort_on_modern);
if (API->external || !gmtapi_skip_this_module (M[module_id].cname)) {
snprintf (message, GMT_LEN256, "%-16s %s\n",
M[module_id].mname, M[module_id].purpose);
GMT_Message (V_API, GMT_TIME_NONE, message);
}
++module_id;
}
}
/* Produce single list on stdout of all GMT core module names for gmt --show-modules */
void gmtlib_module_list_all (void *V_API, struct GMT_MODULEINFO M[]) {
unsigned int module_id = 0;
size_t n_modules = 0;
struct GMTAPI_CTRL *API = gmt_get_api_ptr (V_API);
while (M[n_modules].cname != NULL) /* Count the modules */
++n_modules;
/* Sort array on modern names */
qsort (M, n_modules, sizeof (struct GMT_MODULEINFO), gmtapi_sort_on_modern);
while (M[module_id].cname != NULL) {
if (API->external || !gmtapi_skip_this_module (M[module_id].cname))
printf ("%s\n", M[module_id].mname);
++module_id;
}
}
/* Produce single list on stdout of all GMT core module names for gmt --show-classic [i.e., classic mode names] */
void gmtlib_module_classic_all (void *V_API, struct GMT_MODULEINFO M[]) {
unsigned int module_id = 0;
size_t n_modules = 0;
struct GMTAPI_CTRL *API = gmt_get_api_ptr (V_API);
while (M[n_modules].cname != NULL) /* Count the modules */
++n_modules;
/* Sort array on classic names */
qsort (M, n_modules, sizeof (struct GMT_MODULEINFO), gmtapi_sort_on_classic);
while (M[module_id].cname != NULL) {
if (API->external || !(gmtapi_skip_this_module (M[module_id].cname) || gmtapi_skip_modern_module (M[module_id].cname)))
printf ("%s\n", M[module_id].cname);
++module_id;
}
}
/* Lookup module id by name, return option keys pointer (for external API developers) */
const char *gmtlib_module_keys (void *API, struct GMT_MODULEINFO M[], char *candidate) {
int module_id = 0;
gmt_M_unused(API);
/* Match actual_name against g_module[module_id].cname */
while (M[module_id].cname != NULL &&
strcmp (candidate, M[module_id].cname))
++module_id;
/* Return Module keys or NULL */
return (M[module_id].keys);
}
/* Lookup module id by name, return group char name (for external API developers) */
const char *gmtlib_module_group (void *API, struct GMT_MODULEINFO M[], char *candidate) {
int module_id = 0;
gmt_M_unused(API);
/* Match actual_name against g_module[module_id].cname */
while (M[module_id].cname != NULL &&
strcmp (candidate, M[module_id].cname))
++module_id;
/* Return Module keys or NULL */
return (M[module_id].component);
}
int GMT_Show_ModuleInfo (void *API, struct GMT_MODULEINFO M[], char *arg, unsigned int mode) {
/* API function to display module information from shared libraries */
if (API == NULL) return_error (API, GMT_NOT_A_SESSION);
switch (mode) {
case GMT_MODULE_HELP:
if (arg == NULL) return_error (API, GMT_ARG_IS_NULL);
gmtlib_module_show_all (API, M, arg);
break;
case GMT_MODULE_SHOW_MODERN:
gmtlib_module_list_all (API, M);
break;
case GMT_MODULE_SHOW_CLASSIC:
gmtlib_module_classic_all (API, M);
break;
default:
GMT_Report (API, GMT_MSG_ERROR, "Internal error in GMT_Show_ModuleInfo: Passed bad mode (%d)\n", mode);
return_error (API, GMT_NOT_A_VALID_MODE);
break;
}
return (GMT_NOERROR);
}
const char * GMT_Get_ModuleInfo (void *API, struct GMT_MODULEINFO M[], char *module, unsigned int mode) {
/* API function to display module information from shared libraries */
const char *answer = NULL;
if (API == NULL) return_null (NULL, GMT_NOT_A_SESSION);
if (module == NULL) return_null (NULL, GMT_ARG_IS_NULL);
switch (mode) {
case GMT_MODULE_KEYS:
answer = gmtlib_module_keys (API, M, module);
break;
case GMT_MODULE_GROUP:
answer = gmtlib_module_group (API, M, module);
break;
default:
GMT_Report (API, GMT_MSG_ERROR, "Internal error in GMT_Get_ModuleInfo: Passed bad mode (%d)\n", mode);
return_null (NULL, GMT_NOT_A_VALID_MODE);
break;
}
return (answer);
}
/* Series of one-line functions to assign val to a particular union member of array u at position row, rounding if integer output */
GMT_LOCAL void gmtapi_put_val_double (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->f8[row] = val; }
GMT_LOCAL void gmtapi_put_val_float (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->f4[row] = (float) val; }
GMT_LOCAL void gmtapi_put_val_ulong (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->ui8[row] = (uint64_t)lrint(val); }
GMT_LOCAL void gmtapi_put_val_long (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->si8[row] = (int64_t)lrint(val); }
GMT_LOCAL void gmtapi_put_val_uint (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->ui4[row] = (uint32_t)lrint(val); }
GMT_LOCAL void gmtapi_put_val_int (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->si4[row] = (int32_t)lrint(val); }
GMT_LOCAL void gmtapi_put_val_ushort (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->ui2[row] = (uint16_t)lrint(val); }
GMT_LOCAL void gmtapi_put_val_short (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->si2[row] = (int16_t)lrint(val); }
GMT_LOCAL void gmtapi_put_val_uchar (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->uc1[row] = (uint8_t)lrint(val); }
GMT_LOCAL void gmtapi_put_val_char (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->sc1[row] = (int8_t)lrint(val); }
GMT_LOCAL void gmtapi_get_val_double (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->f8[row]; }
GMT_LOCAL void gmtapi_get_val_float (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->f4[row]; }
GMT_LOCAL void gmtapi_get_val_ulong (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = (double)u->ui8[row]; } /* Must cast/truncate since longs integer range exceed that of double */
GMT_LOCAL void gmtapi_get_val_long (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = (double)u->si8[row]; } /* Must cast/truncate since longs integer range exceed that of double */
GMT_LOCAL void gmtapi_get_val_uint (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->ui4[row]; }
GMT_LOCAL void gmtapi_get_val_int (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->si4[row]; }
GMT_LOCAL void gmtapi_get_val_ushort (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->ui2[row]; }
GMT_LOCAL void gmtapi_get_val_short (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->si2[row]; }
GMT_LOCAL void gmtapi_get_val_uchar (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->uc1[row]; }
GMT_LOCAL void gmtapi_get_val_char (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->sc1[row]; }
GMT_LOCAL inline GMT_putfunction gmtapi_select_put_function (struct GMTAPI_CTRL *API, unsigned int type) {
switch (type) { /* Use type to select the correct put function with which to place a value in the union */
case GMT_DOUBLE: return (gmtapi_put_val_double); break;
case GMT_FLOAT: return (gmtapi_put_val_float); break;
case GMT_ULONG: return (gmtapi_put_val_ulong); break;
case GMT_LONG: return (gmtapi_put_val_long); break;
case GMT_UINT: return (gmtapi_put_val_uint); break;
case GMT_INT: return (gmtapi_put_val_int); break;
case GMT_USHORT: return (gmtapi_put_val_ushort); break;
case GMT_SHORT: return (gmtapi_put_val_short); break;
case GMT_UCHAR: return (gmtapi_put_val_uchar); break;
case GMT_CHAR: return (gmtapi_put_val_char); break;
default:
GMT_Report (API, GMT_MSG_ERROR, "Internal error in gmtapi_select_put_function: Passed bad type (%d), Will be unable to place binary data\n", type);
return NULL;
break;
}
}
GMT_LOCAL inline GMT_getfunction gmtapi_select_get_function (struct GMTAPI_CTRL *API, unsigned int type) {
switch (type) { /* Use type to select the correct get function with which to extract a value from the union */
case GMT_DOUBLE: return (gmtapi_get_val_double); break;
case GMT_FLOAT: return (gmtapi_get_val_float); break;
case GMT_ULONG: return (gmtapi_get_val_ulong); break;
case GMT_LONG: return (gmtapi_get_val_long); break;
case GMT_UINT: return (gmtapi_get_val_uint); break;
case GMT_INT: return (gmtapi_get_val_int); break;
case GMT_USHORT: return (gmtapi_get_val_ushort); break;
case GMT_SHORT: return (gmtapi_get_val_short); break;
case GMT_UCHAR: return (gmtapi_get_val_uchar); break;
case GMT_CHAR: return (gmtapi_get_val_char); break;
default:
GMT_Report (API, GMT_MSG_ERROR, "Internal error in gmtapi_select_get_function: Passed bad type (%d), will be unable to convert binary data\n", type);
return NULL;
break;
}
}
GMT_LOCAL bool gmtapi_valid_input_family (unsigned int family) {
/* Return true for the main input types */
return (family == GMT_IS_DATASET || family == GMT_IS_GRID \
|| family == GMT_IS_IMAGE || family == GMT_IS_PALETTE || family == GMT_IS_POSTSCRIPT);
}
GMT_LOCAL bool gmtapi_valid_actual_family (unsigned int family) {
/* Return true for the main actual family types */
return (family < GMT_N_FAMILIES);
}
GMT_LOCAL bool gmtapi_valid_output_family (unsigned int family) {
if (family == GMT_IS_VECTOR || family == GMT_IS_MATRIX || family == GMT_IS_POSTSCRIPT) return true;
return gmtapi_valid_input_family (family);
}
GMT_LOCAL bool gmtapi_valid_via_family (unsigned int family) {
if (family == GMT_IS_VECTOR || family == GMT_IS_MATRIX) return true;
return false;
}
GMT_LOCAL bool gmtapi_valid_type (int type) { /* Check for valid matrix/vector data types */
if (type < GMT_CHAR || type > GMT_DOUBLE) return false;
return true;
}
/*! . */
GMT_LOCAL int gmtapi_get_item (struct GMTAPI_CTRL *API, unsigned int family, void *data) {
/* Get the first item of requested family from list of objects, allowing for
* datasets and grids to masquerade as other things (Matrix, vector). */
unsigned int i;
int item;
struct GMTAPI_DATA_OBJECT *S_obj = NULL;
API->error = GMT_NOERROR;
for (i = 0, item = GMT_NOTSET; item == GMT_NOTSET && i < API->n_objects; i++) {
if (!API->object[i]) continue; /* Empty object */
S_obj = API->object[i];
if (!S_obj->resource) continue; /* No resource */
if (S_obj->family != (enum GMT_enum_family)family) { /* Not the required data type; check for exceptions... */
if (family == GMT_IS_DATASET && gmtapi_valid_via_family (S_obj->family))
S_obj->family = GMT_IS_DATASET; /* Vectors or Matrix masquerading as dataset are valid. Change their family here. */
else if (family == GMT_IS_GRID && S_obj->family == GMT_IS_MATRIX)
S_obj->family = GMT_IS_GRID; /* Matrix masquerading as grid is valid. Change its family here. */
else /* We don't like your kind */
continue;
}
if (S_obj->resource == data) item = i; /* Found the requested data */
}
if (item == GMT_NOTSET) { API->error = GMT_NOT_A_VALID_ID; return (GMT_NOTSET); } /* No such data found */
return (item);
}
/*! . */
GMT_LOCAL inline uint64_t gmtapi_n_cols_needed_for_gaps (struct GMT_CTRL *GMT, uint64_t n) {
/* Return the actual items needed (which may be more than n if gap testing demands it) */
if (GMT->common.g.active) return (MAX (n, GMT->common.g.n_col)); /* n or n_col (if larger) */
return (n); /* No gap checking, n it is */
}
/*! . */
GMT_LOCAL inline void gmtapi_update_prev_rec (struct GMT_CTRL *GMT, uint64_t n_use) {
/* Update previous record before reading the new record, but only if needed */
if (GMT->current.io.need_previous) gmt_M_memcpy (GMT->current.io.prev_rec, GMT->current.io.curr_rec, n_use, double);
}
/*! . */
GMT_LOCAL int gmtapi_alloc_grid (struct GMT_CTRL *GMT, struct GMT_GRID *G) {
/* Use information in Grid header to allocate the grid data array.
* We assume gmtapi_init_grdheader has already been called. */
struct GMT_GRID_HEADER_HIDDEN *GH = NULL;
if (G == NULL) return (GMT_PTR_IS_NULL);
GH = gmt_get_H_hidden (G->header);
if (G->data) return (GMT_PTR_NOT_NULL);
if (G->header->size == 0U) return (GMT_SIZE_IS_ZERO);
if ((G->data = gmt_M_memory_aligned (GMT, NULL, G->header->size, gmt_grdfloat)) == NULL) return (GMT_MEMORY_ERROR);
GH->orig_datatype = (sizeof (gmt_grdfloat) == sizeof (float)) ? GMT_FLOAT : GMT_DOUBLE;
return (GMT_NOERROR);
}
/*! . */
GMT_LOCAL double * gmtapi_grid_coord (struct GMTAPI_CTRL *API, int dim, struct GMT_GRID *G) {
return (gmt_grd_coord (API->GMT, G->header, dim));
}
/*! . */
GMT_LOCAL int gmtapi_alloc_grid_xy (struct GMTAPI_CTRL *API, struct GMT_GRID *G) {
/* Use information in Grid header to allocate the grid x/y vectors.
* We assume gmtapi_init_grdheader has been called. */
if (G == NULL) return (GMT_PTR_IS_NULL);
if (G->x || G->y) return (GMT_PTR_NOT_NULL);
G->x = gmtapi_grid_coord (API, GMT_X, G); /* Get array of x coordinates */
G->y = gmtapi_grid_coord (API, GMT_Y, G); /* Get array of y coordinates */
return (GMT_NOERROR);
}
/*! . */
GMT_LOCAL int gmtapi_alloc_image (struct GMT_CTRL *GMT, uint64_t *dim, struct GMT_IMAGE *I) {
/* Use information in Image header to allocate the image data.
* We assume gmtapi_init_grdheader has been called.
* If dim given and is 2 or 4 then we have 1 or 3 bands plus alpha channel */
if (I == NULL) return (GMT_PTR_IS_NULL);
if (I->data) return (GMT_PTR_NOT_NULL);
if (I->header->size == 0U) return (GMT_SIZE_IS_ZERO);
if ((I->data = gmt_M_memory_aligned (GMT, NULL, I->header->size * I->header->n_bands, unsigned char)) == NULL) return (GMT_MEMORY_ERROR);
if (dim && (dim[GMT_Z] == 2 || dim[GMT_Z] == 4)) {
if ((I->alpha = gmt_M_memory_aligned (GMT, NULL, I->header->size, unsigned char)) == NULL) return (GMT_MEMORY_ERROR);
}
return (GMT_NOERROR);
}
/*! . */
GMT_LOCAL double * gmtapi_image_coord (struct GMTAPI_CTRL *API, int dim, struct GMT_IMAGE *I) {
return (gmt_grd_coord (API->GMT, I->header, dim));
}
/*! . */
GMT_LOCAL int gmtapi_alloc_image_xy (struct GMTAPI_CTRL *API, struct GMT_IMAGE *I) {
/* Use information in Grid header to allocate the image x,y vectors.
* We assume gmtapi_init_grdheader has been called. */
if (I == NULL) return (GMT_PTR_IS_NULL);
if (I->x || I->y) return (GMT_PTR_NOT_NULL);
I->x = gmtapi_image_coord (API, GMT_X, I); /* Get array of x coordinates */
I->y = gmtapi_image_coord (API, GMT_Y, I); /* Get array of y coordinates */
return (GMT_NOERROR);
}
/*! . */
GMT_LOCAL int gmtapi_print_func (FILE *fp, const char *message) {
/* Just print this message to fp. It is being used indirectly via
* API->print_func. Purpose of this mechanism is to allow external APIs such
* as MATLAB (which cannot use printf) to reset API->print_func to
* mexPrintf or similar functions. Default is gmtapi_print_func. */
fprintf (fp, "%s", message);
return 0;
}
/*! . */
GMT_LOCAL unsigned int gmtapi_gmtry (unsigned int geometry) {
/* Return index to text representation in the array GMT_geometry[] for debug messages */
if (geometry == GMT_IS_POINT) return 1;
if (geometry == GMT_IS_LINE) return 2;
if (geometry == GMT_IS_POLY) return 3;
if (geometry == GMT_IS_PLP) return 4;
if ((geometry & GMT_IS_LINE) && (geometry & GMT_IS_POLY)) return 5;
if (geometry == GMT_IS_SURFACE) return 6;
if (geometry == GMT_IS_NONE) return 7;
if (geometry == GMT_IS_TEXT) return 8;
return 0;
}
/* We also need to return the pointer to an object given a void * address of that pointer.
* This needs to be done on a per data-type basis, e.g., to cast that void * to a struct GMT_GRID **
* so we may return the value at that address: */
GMT_LOCAL inline struct GMTAPI_CTRL * gmtapi_get_api_ptr (struct GMTAPI_CTRL *ptr) {return (ptr);}
GMT_LOCAL inline struct GMT_PALETTE * gmtapi_get_cpt_ptr (struct GMT_PALETTE **ptr) {return (*ptr);}
GMT_LOCAL inline struct GMT_DATASET * gmtapi_get_dataset_ptr (struct GMT_DATASET **ptr) {return (*ptr);}
GMT_LOCAL inline struct GMT_GRID * gmtapi_get_grid_ptr (struct GMT_GRID **ptr) {return (*ptr);}
GMT_LOCAL inline struct GMT_POSTSCRIPT * gmtapi_get_ps_ptr (struct GMT_POSTSCRIPT **ptr) {return (*ptr);}
GMT_LOCAL inline struct GMT_MATRIX * gmtapi_get_matrix_ptr (struct GMT_MATRIX **ptr) {return (*ptr);}
GMT_LOCAL inline struct GMT_VECTOR * gmtapi_get_vector_ptr (struct GMT_VECTOR **ptr) {return (*ptr);}
GMT_LOCAL inline double * gmtapi_get_coord_ptr (double **ptr) {return (*ptr);}
GMT_LOCAL inline struct GMT_IMAGE * gmtapi_get_image_ptr (struct GMT_IMAGE **ptr) {return (*ptr);}
/* Various inline functs to convert void pointer to specific type */
GMT_LOCAL inline struct GMT_GRID_ROWBYROW * gmtapi_get_rbr_ptr (struct GMT_GRID_ROWBYROW *ptr) {return (ptr);}
GMT_LOCAL inline struct GMT_FFT_INFO * gmtapi_get_fftinfo_ptr (struct GMT_FFT_INFO *ptr) {return (ptr);}
GMT_LOCAL inline struct GMT_FFT_WAVENUMBER * gmtapi_get_fftwave_ptr (struct GMT_FFT_WAVENUMBER *ptr) {return (ptr);}
GMT_LOCAL inline struct GMT_FFT_WAVENUMBER ** gmtapi_get_fftwave_addr (struct GMT_FFT_WAVENUMBER **ptr) {return (ptr);}
GMT_LOCAL inline struct GMT_GRID * gmtapi_get_grid_data (struct GMT_GRID *ptr) {return (ptr);}
GMT_LOCAL inline struct GMT_IMAGE * gmtapi_get_image_data (struct GMT_IMAGE *ptr) {return (ptr);}
GMT_LOCAL inline struct GMT_DATASET * gmtapi_get_dataset_data (struct GMT_DATASET *ptr) {return (ptr);}
GMT_LOCAL inline struct GMT_VECTOR * gmtapi_get_vector_data (struct GMT_VECTOR *ptr) {return (ptr);}
GMT_LOCAL inline struct GMT_MATRIX * gmtapi_get_matrix_data (struct GMT_MATRIX *ptr) {return (ptr);}
GMT_LOCAL inline struct GMT_POSTSCRIPT * gmtapi_get_postscript_data (struct GMT_POSTSCRIPT *ptr) {return (ptr);}
GMT_LOCAL inline struct GMT_PALETTE * gmtapi_get_palette_data (struct GMT_PALETTE *ptr) {return (ptr);}
GMT_LOCAL inline char ** gmtapi_get_char_char_ptr (char **ptr) {return (ptr);}
/*! If API is not set or do_not_exit is false then we call system exit, else we move along.
* This is required for some external interfaces where calling exit would bring down the
* external environment (this is true for MATLAB, for instance). */
GMT_LOCAL inline void gmtapi_exit (struct GMTAPI_CTRL *API, int code) {
if (API->do_not_exit == false)
GMT_exit (API->GMT, code);
}
/*! gmtapi_return_address is a convenience function that, given type, calls the correct converter */
GMT_LOCAL void * gmtapi_return_address (void *data, unsigned int type) {
void *p = NULL;
switch (type) {
case GMT_IS_GRID: p = gmtapi_get_grid_ptr (data); break;
case GMT_IS_DATASET: p = gmtapi_get_dataset_ptr (data); break;
case GMT_IS_PALETTE: p = gmtapi_get_cpt_ptr (data); break;
case GMT_IS_POSTSCRIPT: p = gmtapi_get_ps_ptr (data); break;
case GMT_IS_MATRIX: p = gmtapi_get_matrix_ptr (data); break;
case GMT_IS_VECTOR: p = gmtapi_get_vector_ptr (data); break;
case GMT_IS_COORD: p = gmtapi_get_coord_ptr (data); break;
case GMT_IS_IMAGE: p = gmtapi_get_image_ptr (data); break;
}
return (p);
}
/*! . */
struct GMTAPI_CTRL * gmt_get_api_ptr (struct GMTAPI_CTRL *ptr) {
/* Clean casting of void to API pointer at start of a module
* If ptr is NULL we are in deep trouble...
*/
if (ptr == NULL) return_null (NULL, GMT_NOT_A_SESSION);
return (ptr);
}
/*! gmtapi_alloc_object_array is a convenience function that, given type, allocates an array of pointers to the corresponding container */
GMT_LOCAL void * gmtapi_alloc_object_array (struct GMTAPI_CTRL *API, unsigned int n_items, unsigned int type) {
void *p = NULL;
switch (type) {
case GMT_IS_GRID: p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_GRID *); break;
case GMT_IS_DATASET: p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_DATASET *); break;
case GMT_IS_PALETTE: p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_PALETTE *); break;
case GMT_IS_POSTSCRIPT: p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_POSTSCRIPT *); break;
case GMT_IS_IMAGE: p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_IMAGE *); break;
case GMT_IS_MATRIX: p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_MATRIX *); break;
case GMT_IS_VECTOR: p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_VECTOR *); break;
}
return (p);
}
#ifdef DEBUG
/*! Can be used to display API->object info wherever it is called as part of a debug operation */
GMT_LOCAL void gmtapi_list_objects (struct GMTAPI_CTRL *API, char *txt) {
unsigned int item, ext;
struct GMTAPI_DATA_OBJECT *S;
char message[GMT_LEN256] = {""}, O, M;
/* if (API->deep_debug == false) return; */
if (!gmt_M_is_verbose (API->GMT, GMT_MSG_DEBUG)) return;
snprintf (message, GMT_LEN256, "==> %d API Objects at end of %s\n", API->n_objects, txt);
GMT_Message (API, GMT_TIME_NONE, message);
if (API->n_objects == 0) return;
GMT_Message (API, GMT_TIME_NONE, "--------------------------------------------------------\n");
snprintf (message, GMT_LEN256, "K.. ID RESOURCE.... FAMILY.... ACTUAL.... DIR... S O M L\n");
GMT_Message (API, GMT_TIME_NONE, message);
GMT_Message (API, GMT_TIME_NONE, "--------------------------------------------------------\n");
for (item = 0; item < API->n_objects; item++) {
if ((S = API->object[item]) == NULL) continue;
O = (S->no_longer_owner) ? 'N' : 'Y';
M = (S->messenger) ? 'Y' : 'N';
ext = (S->alloc_mode == GMT_ALLOC_EXTERNALLY) ? '*' : ' ';
snprintf (message, GMT_LEN256, "%c%2d %2d %12" PRIxS " %-10s %-10s %-6s %d %c %c %d\n", ext, item, S->ID, (size_t)S->resource,
GMT_family[S->family], GMT_family[S->actual_family], GMT_direction[S->direction], S->status, O, M, S->alloc_level);
GMT_Message (API, GMT_TIME_NONE, message);
}
GMT_Message (API, GMT_TIME_NONE, "--------------------------------------------------------\n");
}
/*! Mostly for debugging */
GMT_LOCAL void gmtapi_set_object (struct GMTAPI_CTRL *API, struct GMTAPI_DATA_OBJECT *obj) {
/* This is mostly for debugging and may go away or remain under DEBUG */
GMT_Report (API, GMT_MSG_DEBUG, "Set_Object for family: %d\n", obj->family);
switch (obj->family) {
case GMT_IS_GRID: obj->G = obj->resource; break;
case GMT_IS_DATASET: obj->D = obj->resource; break;
case GMT_IS_PALETTE: obj->C = obj->resource; break;
case GMT_IS_POSTSCRIPT: obj->P = obj->resource; break;
case GMT_IS_MATRIX: obj->M = obj->resource; break;
case GMT_IS_VECTOR: obj->V = obj->resource; break;
case GMT_IS_COORD: break; /* No worries */
case GMT_IS_IMAGE: obj->I = obj->resource; break;
case GMT_N_FAMILIES: break;
}
}
#endif
GMT_LOCAL void gmtapi_check_for_modern_oneliner (struct GMTAPI_CTRL *API, const char *module, int mode, void *args) {
/* Determine if user is attempting a modern mode one-liner plot, and if so, set run mode to GMT_MODERN.
* This is needed since there is not gmt begin | end sequence in this case.
* Also, if a user wants to get the usage message for a modern mode module then it is also a type
* of one-liner and thus we set to GMT_MODERN as well, but only for modern module names. */
unsigned modern = 0, pos;
char format[GMT_LEN128] = {""}, p[GMT_LEN16] = {""}, *c = NULL;
bool usage = false;
size_t len;
struct GMT_OPTION *opt = NULL, *head = NULL;
if (API->GMT->current.setting.run_mode == GMT_MODERN) { /* Just need to check if a classic name was given... */
if (!strncmp (module, "ps", 2U) && strncmp (module, "psconvert", 9U)) { /* Gave classic ps* name in modern mode but not psconvert */
char not_used[GMT_LEN32] = {""};
const char *mod_name = gmt_current_name (module, not_used);
GMT_Report (API, GMT_MSG_INFORMATION, "Detected a classic module name (%s) in modern mode - please use the modern mode name %s instead.\n", module, mod_name);
}
return; /* Done, since we know it is a modern mode session */
}
head = GMT_Create_Options (API, mode, args); /* Get option list */
if ((opt = GMT_Find_Option (API, 'V', head))) /* Remove -V here so that we can run gmt plot -? -Vd and still get modern mode usage plus debug info */
GMT_Delete_Option (API, opt, &head);
API->GMT->current.setting.use_modern_name = gmtlib_is_modern_name (API, module);
if (API->GMT->current.setting.use_modern_name) { /* Make some checks needed to handle synopsis and usage messages in classic vs modern mode */
if (head == NULL) { /* Gave none or a single argument */
if (API->GMT->current.setting.run_mode == GMT_CLASSIC)
API->usage = true; /* Modern mode name given with no args so not yet in modern mode - allow it to get usage */
return;
}
if (head->next == NULL) { /* Gave a single argument */
if (head->option == GMT_OPT_USAGE || head->option == GMT_OPT_SYNOPSIS || (head->option == '+' && !head->arg[0])) modern = 1;
if (modern) usage = true;
}
}
/* Finally, must check if a one-liner with special graphics format settings were given, e.g., "gmt pscoast -Rg -JH0/15c -Gred -png map" */
for (opt = head; opt; opt = opt->next) {
if (opt->option == GMT_OPT_INFILE || opt->option == GMT_OPT_OUTFILE) continue; /* Skip file names */
if (strchr ("bejpPt", opt->option) == NULL) continue; /* Option not the first letter of a valid graphics format [UPDATE LIST IF ADDING MORE FORMATS IN FUTURE] */
if ((len = strlen (opt->arg)) == 0 || len >= GMT_LEN128) continue; /* No arg or very long args that are filenames can be skipped */
snprintf (format, GMT_LEN128, "%c%s", opt->option, opt->arg); /* Get a local copy so we can mess with it */
if ((c = strchr (format, ','))) c[0] = 0; /* Chop off other formats for the initial id test */
if (gmt_get_graphics_id (API->GMT, format) != GMT_NOTSET) { /* Found a valid graphics format option */
modern = 1; /* Seems like it is, but check the rest of the formats, if there are more */
if (c == NULL) continue; /* Nothing else to check, go to next option */
/* Make sure any other formats are valid, too */
if (c) c[0] = ','; /* Restore any comma we found */
pos = 0;
while (modern && gmt_strtok (format, ",", &pos, p)) { /* Check each format to make sure each is OK */
if (gmt_get_graphics_id (API->GMT, p) == GMT_NOTSET) /* Oh, something wrong was given, cannot be modern */
modern = 0;
}
}
}
if (modern) { /* This is indeed a modern mode one-liner command */
API->GMT->current.setting.run_mode = GMT_MODERN;
API->usage = usage;
}
if (API->GMT->current.setting.run_mode == GMT_MODERN) /* If running in modern mode we want to use modern names */
API->GMT->current.setting.use_modern_name = true;
if (GMT_Destroy_Options (API, &head)) /* Done with these here */
GMT_Report (API, GMT_MSG_WARNING, "Unable to free options in gmtapi_check_for_modern_oneliner?\n");
}
/* Function to get PPID under Windows is a bit different */
#ifdef _WIN32
#include <TlHelp32.h>
GMT_LOCAL int gmtapi_winppid (int pidin) {
/* If pidin == 0 get the PPID of current process
otherwise, get the PPID of pidin process
*/
int pid, ppid = -1;
if (pidin)
pid = pidin;
else
pid = GetCurrentProcessId ();
HANDLE h = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe = { 0 };
pe.dwSize = sizeof (PROCESSENTRY32);
if (Process32First(h, &pe)) {
do {
if (pe.th32ProcessID == (unsigned int)pid)
ppid = pe.th32ParentProcessID;
} while (ppid == -1 && Process32Next(h, &pe));
}
CloseHandle (h);
return (ppid);
}
#endif
/* Safety valve to remove non-alphanumeric characters =*/
GMT_LOCAL char * gmtapi_alnum_only (struct GMTAPI_CTRL *API, char *string) {
unsigned int k = 0, n_changed = 0;
while (string[k]) {
if (!isalnum (string[k])) {
n_changed++;
string[k] = '#';
}
k++;
}
if (n_changed)
GMT_Report (API, GMT_MSG_DEBUG, "Cleaned GMT_SESSION_NAME to %s\n", string);
return (string);
}
/*! . */
GMT_LOCAL char * gmtapi_get_ppid (struct GMTAPI_CTRL *API) {
/* Return the parent process ID [i.e., shell for command line use or gmt app for API] */
int ppid = -1;
unsigned int k = 0;
static char *source[4] = {"GMT_SESSION_NAME", "parent", "app", "hardwired choice"};
char *str = NULL, string[GMT_LEN8];
if ((str = getenv ("GMT_SESSION_NAME")) != NULL) { /* GMT_SESSION_NAME was set in the environment */
char *tmp = strdup (str); /* Duplicate the given string */
GMT_Report (API, GMT_MSG_DEBUG, "Obtained GMT_SESSION_NAME from the environment: %s\n", str);
return (gmtapi_alnum_only (API, tmp)); /* Replace any non-alphanumeric characters with # */
}
/* Here we just need to get the PPID and format to string */
#ifdef DEBUG_MODERN /* To simplify debugging we set it to 1 */
if (ppid == -1) ppid = 1, k = 3;
#elif defined(WIN32)
/* OK, the trouble is the following. On Win, if the Windows executables are run from within a bash window
gmtapi_get_ppid returns different values for each call, and this completely breaks the idea
of using the constant PPID (parent PID) to create unique file names for each session.
So, given that we didn't yet find a way to make this work from within MSYS (and likely Cygwin)
we are forcing PPID = 0 in all Windows variants unless set via GMT_SESSION_NAME. A corollary of this
is that Windows users running many bash windows concurrently should use GMT_SESSION_NAME in their scripts
to give unique values to different scripts. */
if ((str = getenv ("SHELL")) != NULL) { /* GMT_SESSION_NAME was set in the environment */
//if (ppid == -1) ppid = 0, k = 3;
ppid = gmtapi_winppid(0); /* First time get PPID of current process */
ppid = gmtapi_winppid(ppid); /* Second time get PPPID of current process */
k = 1;
}
else {
if (ppid == -1) ppid = gmtapi_winppid(0), k = 1;
}
#else /* Normal situation */
else if (API->external) /* Return PID of the controlling app instead for external interfaces */
ppid = getpid (), k = 2;
else /* Here we are probably running from the command line and want the shell's PID */
ppid = getppid(), k = 1; /* parent process id */
#endif
GMT_Report (API, GMT_MSG_DEBUG, "Obtained the ppid from %s: %d\n", source[k], ppid);
snprintf (string, GMT_LEN8, "%d", ppid);
return (strdup (string));
}
/*! . */
GMT_LOCAL char * gmtapi_lib_tag (char *name) {
/* Pull out the tag from a name like <tag>[.extension] */
char *extension = NULL, *pch = NULL, *tag = NULL;
if (!strchr (name, '.')) return NULL; /* No file with extension given, probably just a directory due to user confusion */
tag = strdup (name);
extension = strrchr (tag, '.'); /* last period in name */
if (extension) *extension = '\0'; /* remove extension */
/* if name has the "_w32|64" suffix or any other suffix that starts with a '_', remove it. */
pch = strrchr(tag, '_');
if (pch) *pch = '\0';
return (tag);
}
/*! . */
GMT_LOCAL int gmtapi_init_sharedlibs (struct GMTAPI_CTRL *API) {
/* At the end of GMT_Create_Session we are done with processing gmt.conf.
* We can now determine how many shared libraries and plugins to consider, and open the core lib */
struct GMT_CTRL *GMT = API->GMT;
unsigned int n_custom_libs, k, e, n_alloc = GMT_TINY_CHUNK;
char text[PATH_MAX] = {""}, plugindir[PATH_MAX] = {""}, path[PATH_MAX] = {""};
char *libname = NULL, **list = NULL;
#ifdef WIN32
char *extension[1] = {".dll"};
unsigned int n_extensions = 1;
#elif defined(__APPLE__) /* Look for both .so and .dylib shared libs on OSX */
char *extension[2] = {".so", ".dylib"};
unsigned int n_extensions = 2;
#else /* Linux, etc. only use .so */
char *extension[1] = {".so"};
unsigned int n_extensions = 1;
#endif
#ifdef SUPPORT_EXEC_IN_BINARY_DIR
/* If SUPPORT_EXEC_IN_BINARY_DIR is defined we try to load plugins from the
* build tree */
/* Only true, when we are running in a subdir of GMT_BINARY_DIR_SRC_DEBUG: */
bool running_in_bindir_src = !strncmp (GMT->init.runtime_bindir, GMT_BINARY_DIR_SRC_DEBUG, strlen(GMT_BINARY_DIR_SRC_DEBUG));
#endif
API->lib = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_LIBINFO);
/* 1. Load the GMT core library by default [unless libgmt is used externally] */
/* Note: To extract symbols from the currently executing process we need to load it as a special library.
* This is done by passing NULL under Linux and by calling GetModuleHandleEx under Windows, hence we
* use the dlopen_special call which is defined in gmt_sharedlibs.c. If the gmt core and supplemental
* libraries are being used by 3rd party externals then no library is special and they are all opened
* the first time we need access. */
API->lib[0].name = strdup ("core");
n_custom_libs = 1; /* Always have at least one shared gmt library */
if (API->external) { /* Determine the path to this library */
if (GMT->init.runtime_libdir) { /* Successfully determined runtime dir for shared libs */
sprintf (path, "%s/%s", GMT->init.runtime_libdir, GMT_CORE_LIB_NAME);
API->lib[0].path = strdup (path);
}
else /* Rely on the OS to find it */
API->lib[0].path = strdup (GMT_CORE_LIB_NAME);
}
else { /* The handling of the core library is only special when gmt.c is used. */
API->lib[0].path = strdup (GMT_CORE_LIB_NAME);
GMT_Report (API, GMT_MSG_DEBUG, "Loading core GMT shared library: %s\n", API->lib[0].path);
if ((API->lib[0].handle = dlopen_special (API->lib[0].path)) == NULL) {
GMT_Report (API, GMT_MSG_ERROR, "Failure while loading core GMT shared library: %s\n", dlerror());