[segyio] 341/376: Add application segyio-crop
Jørgen Kvalsvik
jokva-guest at moszumanska.debian.org
Wed Sep 20 08:04:55 UTC 2017
This is an automated email from the git hooks/post-receive script.
jokva-guest pushed a commit to branch debian
in repository segyio.
commit afe340be64d40aa3845a969d374374d459230423
Author: Jørgen Kvalsvik <jokva at statoil.com>
Date: Thu Jul 13 15:46:13 2017 +0200
Add application segyio-crop
segyio-crop writes copies a sub cube into a new SEG-Y file.
---
applications/CMakeLists.txt | 8 +
applications/segyio-crop.c | 463 ++++++++++++++++++++++++++++++++++++++++++++
cppcheck/suppressions.txt | 4 +
man/CMakeLists.txt | 1 +
man/segyio-crop.1 | 90 +++++++++
5 files changed, 566 insertions(+)
diff --git a/applications/CMakeLists.txt b/applications/CMakeLists.txt
index 1b146da..cb19a71 100644
--- a/applications/CMakeLists.txt
+++ b/applications/CMakeLists.txt
@@ -25,7 +25,15 @@ target_compile_definitions(segyio-cath PRIVATE
-Dsegyio_MINOR=${segyio_MINOR}
)
+add_executable(segyio-crop segyio-crop.c)
+target_link_libraries(segyio-crop segyio apputils)
+target_compile_definitions(segyio-crop PRIVATE
+ -Dsegyio_MAJOR=${segyio_MAJOR}
+ -Dsegyio_MINOR=${segyio_MINOR}
+)
+
install(TARGETS segyinfo DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
install(TARGETS segyinspect DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
install(TARGETS segyio-cath DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(TARGETS segyio-crop DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/applications/segyio-crop.c b/applications/segyio-crop.c
new file mode 100644
index 0000000..9cf1364
--- /dev/null
+++ b/applications/segyio-crop.c
@@ -0,0 +1,463 @@
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "apputils.c"
+#include <segyio/segy.h>
+
+static int help() {
+ puts( "Usage: segyio-crop [OPTION]... SRC DST\n"
+ "Copy a sub cube from SRC to DST\n"
+ "\n"
+ "-i, --iline-begin=LINE inline to copy from\n"
+ "-I, --iline-end=LINE inline to copy to (inclusive)\n"
+ "-x, --xline-begin=LINE crossline to copy from\n"
+ "-X, --xline-end=LINE crossline to copy to (inclusive)\n"
+ " --inline-begin alias to --iline-begin\n"
+ " --crossline-begin alias to --xline-begin\n"
+ "-s, --sample-begin=TIME measurement to copy from\n"
+ "-S, --sample-end=TIME measurement to copy to (inclusive)\n"
+ "-b, --il inline header word byte offset\n"
+ "-B, --xl crossline header word byte offset\n"
+ "-v, --verbose increase verbosity\n"
+ " --version ouput version information and exit\n"
+ " --help display this help and exit\n"
+ "\n"
+ "If no begin/end options are specified, this program is\n"
+ "essentially a copy. If a begin option is omitted, the program\n"
+ "copies from the start. If an end option is omitted, the program\n"
+ "copies until the end.\n"
+ );
+ return 0;
+}
+
+static int printversion() {
+ printf( "segyio-crop (segyio version %d.%d)\n", segyio_MAJOR, segyio_MINOR );
+ return 0;
+}
+
+struct delay {
+ int delay;
+ int skip;
+ int len;
+};
+
+static struct delay delay_recording_time( const char* trheader,
+ int sbeg,
+ int send,
+ int dt,
+ int samples ) {
+
+ long long t0 = trfield( trheader, SEGY_TR_DELAY_REC_TIME );
+ int trdt = trfield( trheader, SEGY_TR_SAMPLE_INTER );
+ if( trdt ) dt = trdt;
+
+ /*
+ * begin/end not specified - copy the full trace, so dont try to identify
+ * the sub trace
+ */
+ struct delay d = { t0, 0, samples };
+ if( sbeg < 0 && send == INT_MAX ) return d;
+
+ /* determine what to cut off at the start of the trace */
+ if( sbeg - t0 > 0 ) {
+ long long skip = ((sbeg - t0) * 1000) / dt;
+ d.delay = t0 + ((skip * dt) / 1000 );
+ d.skip = skip;
+ d.len -= d.skip;
+ }
+
+ /* determine what to cut off at the end of the trace */
+ if( (long long)send * 1000 < (t0 * 1000) + (samples * dt) ) {
+ long long t0us = t0 * 1000;
+ long long sendus = (long long)send * 1000;
+ d.len -= (t0us + ((samples - 1) * dt) - sendus) / dt;
+ }
+
+ return d;
+}
+
+static int valid_trfield( int x ) {
+ switch( x ) {
+ case SEGY_TR_SEQ_LINE:
+ case SEGY_TR_SEQ_FILE:
+ case SEGY_TR_FIELD_RECORD:
+ case SEGY_TR_NUMBER_ORIG_FIELD:
+ case SEGY_TR_ENERGY_SOURCE_POINT:
+ case SEGY_TR_ENSEMBLE:
+ case SEGY_TR_NUM_IN_ENSEMBLE:
+ case SEGY_TR_TRACE_ID:
+ case SEGY_TR_SUMMED_TRACES:
+ case SEGY_TR_STACKED_TRACES:
+ case SEGY_TR_DATA_USE:
+ case SEGY_TR_OFFSET:
+ case SEGY_TR_RECV_GROUP_ELEV:
+ case SEGY_TR_SOURCE_SURF_ELEV:
+ case SEGY_TR_SOURCE_DEPTH:
+ case SEGY_TR_RECV_DATUM_ELEV:
+ case SEGY_TR_SOURCE_DATUM_ELEV:
+ case SEGY_TR_SOURCE_WATER_DEPTH:
+ case SEGY_TR_GROUP_WATER_DEPTH:
+ case SEGY_TR_ELEV_SCALAR:
+ case SEGY_TR_SOURCE_GROUP_SCALAR:
+ case SEGY_TR_SOURCE_X:
+ case SEGY_TR_SOURCE_Y:
+ case SEGY_TR_GROUP_X:
+ case SEGY_TR_GROUP_Y:
+ case SEGY_TR_COORD_UNITS:
+ case SEGY_TR_WEATHERING_VELO:
+ case SEGY_TR_SUBWEATHERING_VELO:
+ case SEGY_TR_SOURCE_UPHOLE_TIME:
+ case SEGY_TR_GROUP_UPHOLE_TIME:
+ case SEGY_TR_SOURCE_STATIC_CORR:
+ case SEGY_TR_GROUP_STATIC_CORR:
+ case SEGY_TR_TOT_STATIC_APPLIED:
+ case SEGY_TR_LAG_A:
+ case SEGY_TR_LAG_B:
+ case SEGY_TR_DELAY_REC_TIME:
+ case SEGY_TR_MUTE_TIME_START:
+ case SEGY_TR_MUTE_TIME_END:
+ case SEGY_TR_SAMPLE_COUNT:
+ case SEGY_TR_SAMPLE_INTER:
+ case SEGY_TR_GAIN_TYPE:
+ case SEGY_TR_INSTR_GAIN_CONST:
+ case SEGY_TR_INSTR_INIT_GAIN:
+ case SEGY_TR_CORRELATED:
+ case SEGY_TR_SWEEP_FREQ_START:
+ case SEGY_TR_SWEEP_FREQ_END:
+ case SEGY_TR_SWEEP_LENGTH:
+ case SEGY_TR_SWEEP_TYPE:
+ case SEGY_TR_SWEEP_TAPERLEN_START:
+ case SEGY_TR_SWEEP_TAPERLEN_END:
+ case SEGY_TR_TAPER_TYPE:
+ case SEGY_TR_ALIAS_FILT_FREQ:
+ case SEGY_TR_ALIAS_FILT_SLOPE:
+ case SEGY_TR_NOTCH_FILT_FREQ:
+ case SEGY_TR_NOTCH_FILT_SLOPE:
+ case SEGY_TR_LOW_CUT_FREQ:
+ case SEGY_TR_HIGH_CUT_FREQ:
+ case SEGY_TR_LOW_CUT_SLOPE:
+ case SEGY_TR_HIGH_CUT_SLOPE:
+ case SEGY_TR_YEAR_DATA_REC:
+ case SEGY_TR_DAY_OF_YEAR:
+ case SEGY_TR_HOUR_OF_DAY:
+ case SEGY_TR_MIN_OF_HOUR:
+ case SEGY_TR_SEC_OF_MIN:
+ case SEGY_TR_TIME_BASE_CODE:
+ case SEGY_TR_WEIGHTING_FAC:
+ case SEGY_TR_GEOPHONE_GROUP_ROLL1:
+ case SEGY_TR_GEOPHONE_GROUP_FIRST:
+ case SEGY_TR_GEOPHONE_GROUP_LAST:
+ case SEGY_TR_GAP_SIZE:
+ case SEGY_TR_OVER_TRAVEL:
+ case SEGY_TR_CDP_X:
+ case SEGY_TR_CDP_Y:
+ case SEGY_TR_INLINE:
+ case SEGY_TR_CROSSLINE:
+ case SEGY_TR_SHOT_POINT:
+ case SEGY_TR_SHOT_POINT_SCALAR:
+ case SEGY_TR_MEASURE_UNIT:
+ case SEGY_TR_TRANSDUCTION_MANT:
+ case SEGY_TR_TRANSDUCTION_EXP:
+ case SEGY_TR_TRANSDUCTION_UNIT:
+ case SEGY_TR_DEVICE_ID:
+ case SEGY_TR_SCALAR_TRACE_HEADER:
+ case SEGY_TR_SOURCE_TYPE:
+ case SEGY_TR_SOURCE_ENERGY_DIR_MANT:
+ case SEGY_TR_SOURCE_ENERGY_DIR_EXP:
+ case SEGY_TR_SOURCE_MEASURE_MANT:
+ case SEGY_TR_SOURCE_MEASURE_EXP:
+ case SEGY_TR_SOURCE_MEASURE_UNIT:
+ case SEGY_TR_UNASSIGNED1:
+ case SEGY_TR_UNASSIGNED2:
+ return 1;
+
+ default: return 0;
+ }
+}
+
+#define TRHSIZE SEGY_TRACE_HEADER_SIZE
+#define BINSIZE SEGY_BINARY_HEADER_SIZE
+#define TEXTSIZE SEGY_TEXT_HEADER_SIZE
+
+struct options {
+ int ibeg, iend;
+ int xbeg, xend;
+ int sbeg, send;
+ int il, xl;
+ char* src;
+ char* dst;
+ int verbosity;
+ int version, help;
+ const char* errmsg;
+};
+
+static struct options parse_options( int argc, char** argv ) {
+ struct options opts;
+ opts.ibeg = -1, opts.iend = INT_MAX;
+ opts.xbeg = -1, opts.xend = INT_MAX;
+ opts.sbeg = -1, opts.send = INT_MAX;
+ opts.il = SEGY_TR_INLINE, opts.xl = SEGY_TR_CROSSLINE;
+ opts.verbosity = 0;
+ opts.version = 0, opts.help = 0;
+ opts.errmsg = NULL;
+
+ struct options opthelp, optversion;
+ opthelp.help = 1, opthelp.errmsg = NULL;
+ optversion.version = 1, optversion.errmsg = NULL;
+
+ static struct option long_options[] = {
+ { "iline-begin", required_argument, 0, 'i' },
+ { "iline-end", required_argument, 0, 'I' },
+ { "inline-begin", required_argument, 0, 'i' },
+ { "inline-end", required_argument, 0, 'I' },
+
+ { "xline-begin", required_argument, 0, 'x' },
+ { "xline-end", required_argument, 0, 'X' },
+ { "crossline-begin", required_argument, 0, 'x' },
+ { "crossline-end", required_argument, 0, 'X' },
+
+ { "sample-begin", required_argument, 0, 's' },
+ { "sample-end", required_argument, 0, 'S' },
+
+ { "il", required_argument, 0, 'b' },
+ { "xl", required_argument, 0, 'B' },
+
+ { "verbose", no_argument, 0, 'v' },
+ { "version", no_argument, 0, 'V' },
+ { "help", no_argument, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+
+ static const char* parsenum_errmsg[] = { "", "num must be an integer",
+ "num must be non-negative" };
+
+ opterr = 1;
+
+ while( true ) {
+ int option_index = 0;
+ int c = getopt_long( argc, argv, "vi:I:x:X:s:S:b:B:",
+ long_options, &option_index );
+
+ if( c == -1 ) break;
+
+ int ret;
+ switch( c ) {
+ case 0: break;
+ case 'h': return opthelp;
+ case 'V': return optversion;
+ case 'v': ++opts.verbosity; break;
+
+ case 'i':
+ ret = parseint( optarg, &opts.ibeg );
+ if( ret == 0 ) break;
+ opts.errmsg = parsenum_errmsg[ ret ];
+ return opts;
+
+ case 'I':
+ ret = parseint( optarg, &opts.iend );
+ if( ret == 0 ) break;
+ opts.errmsg = parsenum_errmsg[ ret ];
+ return opts;
+
+ case 'x':
+ ret = parseint( optarg, &opts.xbeg );
+ if( ret == 0 ) break;
+ opts.errmsg = parsenum_errmsg[ ret ];
+ return opts;
+
+ case 'X':
+ ret = parseint( optarg, &opts.xend );
+ if( ret == 0 ) break;
+ opts.errmsg = parsenum_errmsg[ ret ];
+ return opts;
+
+ case 's':
+ ret = parseint( optarg, &opts.sbeg );
+ if( ret == 0 ) break;
+ opts.errmsg = parsenum_errmsg[ ret ];
+ return opts;
+
+ case 'S':
+ ret = parseint( optarg, &opts.send );
+ if( ret == 0 ) break;
+ opts.errmsg = parsenum_errmsg[ ret ];
+ return opts;
+
+ case 'b':
+ ret = parseint( optarg, &opts.il );
+ if( ret == 0 ) break;
+ opts.errmsg = parsenum_errmsg[ ret ];
+ return opts;
+
+ case 'B':
+ ret = parseint( optarg, &opts.xl );
+ if( ret == 0 ) break;
+ opts.errmsg = parsenum_errmsg[ ret ];
+ return opts;
+
+ default:
+ opthelp.errmsg = "";
+ return opthelp;
+ }
+ }
+
+ if( argc - optind != 2 ) {
+ errmsg( 0, "Wrong number of files" );
+ return opthelp;
+ }
+
+ opts.src = argv[ optind + 0 ];
+ opts.dst = argv[ optind + 1 ];
+ return opts;
+}
+
+int main( int argc, char** argv ) {
+
+ struct options opts = parse_options( argc, argv );
+
+ if( opts.help ) return help() + (opts.errmsg ? 2 : 0);
+ if( opts.version ) return printversion();
+ if( opts.errmsg ) return errmsg( EINVAL, opts.errmsg );
+
+ int ibeg = opts.ibeg;
+ int iend = opts.iend;
+ int xbeg = opts.xbeg;
+ int xend = opts.xend;
+ int sbeg = opts.sbeg;
+ int send = opts.send;
+ int il = opts.il;
+ int xl = opts.xl;
+ int verbosity = opts.verbosity;
+
+ if( !valid_trfield( il ) )
+ return errmsg( -3, "Invalid inline byte offset" );
+
+ if( !valid_trfield( xl ) )
+ return errmsg( -3, "Invalid crossline byte offset" );
+
+ if( ibeg > iend )
+ return errmsg( -4, "Invalid iline interval - file would be empty" );
+
+ if( xbeg > xend )
+ return errmsg( -4, "Invalid xline interval - file would be empty" );
+
+ if( sbeg > send )
+ return errmsg( -4, "Invalid sample interval - file would be empty" );
+
+ char textheader[ TEXTSIZE ] = { 0 };
+ char binheader[ BINSIZE ] = { 0 };
+ char trheader[ TEXTSIZE ] = { 0 };
+
+ FILE* src = fopen( opts.src, "rb" );
+ if( !src )
+ return errmsg2( errno, "Unable to open src", strerror( errno ) );
+
+ FILE* dst = fopen( opts.dst, "wb" );
+ if( !dst )
+ return errmsg2( errno, "Unable to open dst", strerror( errno ) );
+
+ /* copy the textual and binary headers */
+ if( verbosity > 0 ) puts( "Copying text header" );
+ int sz;
+ sz = fread( textheader, TEXTSIZE, 1, src );
+ if( sz != 1 ) return errmsg2( errno, "Unable to read text header",
+ strerror( errno ) );
+
+ sz = fwrite( textheader, TEXTSIZE, 1, dst );
+ if( sz != 1 ) return errmsg2( errno, "Unable to write text header",
+ strerror( errno ) );
+
+ if( verbosity > 0 ) puts( "Copying binary header" );
+ sz = fread( binheader, BINSIZE, 1, src );
+ if( sz != 1 ) return errmsg2( errno, "Unable to read binary header",
+ strerror( errno ) );
+
+ sz = fwrite( binheader, BINSIZE, 1, dst );
+ if( sz != 1 ) return errmsg2( errno, "Unable to write binary header",
+ strerror( errno ) );
+
+ int ext_headers = bfield( binheader, SEGY_BIN_EXT_HEADERS );
+ if( ext_headers < 0 ) return errmsg( -1, "Malformed binary header" );
+
+ for( int i = 0; i < ext_headers; ++i ) {
+ if( verbosity > 0 ) puts( "Copying extended text header" );
+ sz = fread( textheader, TEXTSIZE, 1, src );
+ if( sz != 1 ) return errmsg2( errno, "Unable to read ext text header",
+ strerror( errno ) );
+
+ sz = fwrite( textheader, TEXTSIZE, 1, dst );
+ if( sz != 1 ) return errmsg2( errno, "Unable to write ext text header",
+ strerror( errno ) );
+ }
+
+ if( verbosity > 2 ) puts( "Computing samples-per-trace" );
+ const int bindt = bfield( binheader, SEGY_BIN_INTERVAL );
+ const int src_samples = bfield( binheader, SEGY_BIN_SAMPLES );
+ if( src_samples < 0 )
+ return errmsg( -2, "Could not determine samples per trace" );
+
+ if( verbosity > 2 ) printf( "Found %d samples per trace\n", src_samples );
+
+ float* trace = malloc( src_samples * sizeof( float ) );
+
+ if( verbosity > 0 ) puts( "Copying traces" );
+ long long traces = 0;
+ while( true ) {
+ sz = fread( trheader, TRHSIZE, 1, src );
+
+ if( sz != 1 && feof( src ) ) break;
+ if( sz != 1 && ferror( src ) )
+ return errmsg( ferror( src ), "Unable to read trace header" );
+
+ int ilno = trfield( trheader, SEGY_TR_INLINE );
+ int xlno = trfield( trheader, SEGY_TR_CROSSLINE );
+
+ /* outside copy interval - skip this trace */
+ if( ilno < ibeg || ilno > iend || xlno < xbeg || xlno > xend ) {
+ fseek( src, sizeof( float ) * src_samples, SEEK_CUR );
+ continue;
+ }
+
+ sz = fread( trace, sizeof( float ), src_samples, src );
+ if( sz != src_samples )
+ return errmsg2( errno, "Unable to read trace",
+ strerror( errno ) );
+
+ /* figure out how to crop the trace */
+ struct delay d = delay_recording_time( trheader,
+ sbeg,
+ send,
+ bindt,
+ src_samples );
+
+ segy_set_field( trheader, SEGY_TR_DELAY_REC_TIME, d.delay );
+ segy_set_bfield( binheader, SEGY_BIN_SAMPLES, d.len );
+
+ if( verbosity > 2 ) printf( "Copying trace %lld\n", traces );
+ sz = fwrite( trheader, TRHSIZE, 1, dst );
+ if( sz != 1 )
+ return errmsg2( errno, "Unable to write trace header",
+ strerror( errno ) );
+
+ sz = fwrite( trace + d.skip, sizeof( float ), d.len, dst );
+ if( sz != d.len )
+ return errmsg2( errno, "Unable to write trace",
+ strerror( errno ) );
+ ++traces;
+ }
+
+ fseek( dst, SEGY_TEXT_HEADER_SIZE, SEEK_SET );
+ sz = fwrite( binheader, BINSIZE, 1, dst );
+ if( sz != 1 ) return errmsg2( errno, "Unable to write binary header",
+ strerror( errno ) );
+
+ free( trace );
+ fclose( dst );
+ fclose( src );
+}
diff --git a/cppcheck/suppressions.txt b/cppcheck/suppressions.txt
index c787b84..be636cd 100644
--- a/cppcheck/suppressions.txt
+++ b/cppcheck/suppressions.txt
@@ -3,3 +3,7 @@ preprocessorErrorDirective:*Python.h
// silence unreachable-warnings generated by the exit(1) call in unittest
unreachableCode:*test/*.c
+
+// resource leaks in segyio-crop are unproblematic because they happen in
+// teardown
+resourceLeak:*segyio-crop.c
diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt
index 8b43cbe..56cbca2 100644
--- a/man/CMakeLists.txt
+++ b/man/CMakeLists.txt
@@ -5,5 +5,6 @@ if(NOT UNIX)
endif()
install(FILES segyio-cath.1
+ segyio-crop.1
DESTINATION ${CMAKE_INSTALL_MANDIR}
)
diff --git a/man/segyio-crop.1 b/man/segyio-crop.1
new file mode 100644
index 0000000..c8faff8
--- /dev/null
+++ b/man/segyio-crop.1
@@ -0,0 +1,90 @@
+.TH SEGYIO-CROP 1
+.SH NAME
+segyio-crop \- Copy a sub cube from SRC to DST
+.SH SYNPOSIS
+.B segyio-crop
+[\fIOPTION\fR]...
+\fISOURCE DEST\fR
+.SH DESCRIPTION
+.B segyio-crop
+Copy a sub cube from SEG-Y file SOURCE to DEST.
+
+.PP
+If no \fBbegin\fR or \fBend\fR options are specified, this program is
+essentially a copy. If a \fBbegin\fR option is omitted, the program copies from
+the start of the file. If an \fBend\fR option is omitted, the program copies
+until the end of file is reached.
+
+.PP
+Mandatory arguments to long options are mandatory for short options too.
+
+.SH OPTIONS
+.TP
+.BR \-i ", " \-\-iline-begin =\fILINE\fR
+inline to copy from
+
+.TP
+.BR \-I ", " \-\-iline-end=\fILINE\fR
+inline to copy to (inclusive)
+
+.TP
+.BR \-x ", " \-\-xline-begin =\fILINE\fR
+crossline to copy from
+
+.TP
+.BR \-X ", " \-\-xline-end=\fILINE\fR
+crossnline to copy to (inclusive)
+
+.TP
+.BR \-\-inline-begin =\fILINE\fR
+alias to \-\-iline-begin
+
+.TP
+.BR \-\-inline-end =\fILINE\fR
+alias to \-\-iline-end
+
+.TP
+.BR \-\-crossline-begin =\fILINE\fR
+alias to \-\-xline-begin
+
+.TP
+.BR \-\-crossline-end =\fILINE\fR
+alias to \-\-xline-end
+
+.TP
+.BR \-s ", " \-\-sample-begin =\fILINE\fR
+sample to copy from
+
+.TP
+.BR \-S ", " \-\-sample-end=\fILINE\fR
+sample to copy to (inclusive)
+
+.TP
+.BR \-b ", " \-\-il =\fINUM\fR
+inline header word byte offset; must align with SEG-Y defined offsets
+
+defaults to 189
+
+.TP
+.BR \-B ", " \-\-xl =\fINUM\fR
+crossline header word byte offset; must align with SEG-Y defined offsets
+
+defaults to 193
+
+.TP
+.BR \-v ", " \-\-verbose
+increase verbosity
+
+.TP
+.BR \-\-version
+output version information and exit
+
+.TP
+.BR \-\-help
+display this help and exit
+
+.SH COPYRIGHT
+Copyright © Statoil ASA. License LPLv3+: GNU LGPL version 3 or later <http://gnu.org/licenses/lgpl.html>.
+
+.PP
+This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/segyio.git
More information about the debian-science-commits
mailing list