SDDSlib
Loading...
Searching...
No Matches
sddssequence.c
Go to the documentation of this file.
1/**
2 * @file sddssequence.c
3 * @brief Generates an SDDS file with equispaced indices in a column.
4 *
5 * This program creates an SDDS file containing a single page with one or more columns of data.
6 * The columns can be defined, and sequences of values can be generated using user-specified parameters.
7 * Multiple sequences, repeats, and page breaks are supported.
8 *
9 * ### Key Features:
10 * - Define columns using the `-define` option.
11 * - Generate sequences with the `-sequence` option, specifying start, end, delta, and interval values.
12 * - Repeat data sequences with the `-repeat` option.
13 * - Insert page breaks between repeats with the `-break` option.
14 * - Support row-major and column-major order for data storage with the `-majorOrder` option.
15 *
16 * ### Usage:
17 * ```
18 * sddssequence [<outputfile>] \
19 * [-pipe=<output>] \
20 * -define=<columnName>[,<definitionEntries>] \
21 * [-repeat=<number>] \
22 * [-break] \
23 * -sequence=begin=<value>[,number=<integer>][,end=<value>][,delta=<value>][,interval=<integer>] \
24 * [-sequence=...] [-majorOrder=row|column]
25 * ```
26 *
27 * @details
28 * - **Options**:
29 * - **`-define`**: Define columns and their attributes.
30 * - **`-sequence`**: Specify sequences of data with flexible parameters.
31 * - **`-repeat`**: Repeat sequences a specified number of times.
32 * - **`-break`**: Insert page breaks between repeats.
33 * - **`-majorOrder`**: Choose between row-major or column-major data storage.
34 *
35 * - **Example**:
36 * ```
37 * sddssequence output.sdds -define=x -sequence=begin=0,delta=1,number=10
38 * ```
39 * Generates a file `output.sdds` with a column `x` containing values 0 to 9.
40 *
41 * @copyright
42 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
43 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
44 *
45 * @license
46 * This file is distributed under the terms of the Software License Agreement
47 * found in the file LICENSE included with this distribution.
48 *
49 * @author M. Borland, C. Saunders, L. Emery, R. Soliday, H. Shang
50 */
51
52#include <stdlib.h>
53#include "mdb.h"
54#include "SDDS.h"
55#include "scan.h"
56
57/* Enumeration for option types */
58enum option_type {
59 SET_PIPE,
60 SET_DEFINE,
61 SET_SEQUENCE,
62 SET_REPEAT,
63 SET_MAJOR_ORDER,
64 SET_BREAK,
65 N_OPTIONS
66};
67
68char *option[N_OPTIONS] = {
69 "pipe",
70 "define",
71 "sequence",
72 "repeat",
73 "majorOrder",
74 "break",
75};
76
77/* Improved usage message with enhanced readability */
78static char *USAGE =
79 "Usage: sddssequence [<outputfile>] \\\n"
80 " [-pipe=<output>] \\\n"
81 " -define=<columnName>[,<definitionEntries>] \\\n"
82 " [-repeat=<number>] \\\n"
83 " [-break] \\\n"
84 " -sequence=begin=<value>[,number=<integer>][,end=<value>][,delta=<value>][,interval=<integer>] \\\n"
85 " [-sequence=begin=<value>[,number=<integer>][,end=<value>][,delta=<value>][,interval=<integer>] ...] \\\n"
86 " [-majorOrder=row|column]\n\n"
87 "Generates an SDDS file with a single page and several columns of data.\n"
88 "Options:\n"
89 " <outputfile> Specify the output SDDS file. If omitted, standard output is used.\n"
90 " -pipe=<output> Define pipe output options.\n"
91 " -define=<columnName>,<entries> Define a column with the given name and entries.\n"
92 " -repeat=<number> Repeat the sequence the specified number of times.\n"
93 " -break Insert a page break between repeats.\n"
94 " -sequence=begin=<val>,number=<n>,end=<val>,delta=<val>,interval=<n>\n"
95 " Define a sequence with specified parameters. Multiple -sequence options can be used.\n"
96 " -majorOrder=row|column Set the major order of data storage.\n\n"
97 "Notes:\n"
98 " - The default data type is double. To specify a different type, use type=<typeName> in -define.\n"
99 " - Each column is specified with a -define option followed by any number of -sequence options.\n"
100 " - The default value of delta is 1.\n"
101 " - The default beginning value is the ending value of the last sequence plus the delta of the last sequence.\n\n"
102 "Program by Michael Borland. (" __DATE__ " " __TIME__ ", SVN revision: " SVN_VERSION ")\n";
103
104#define SEQ_END_GIVEN 0x0001
105#define SEQ_BEGIN_GIVEN 0x0002
106#define SEQ_NUMBER_GIVEN 0x0004
107#define SEQ_DELTA_GIVEN 0x0008
108#define SEQ_INTERVAL_GIVEN 0x0010
109
110/* Valid combinations of end, number, and delta: */
111#define SEQ_ENDplusDELTA (SEQ_END_GIVEN + SEQ_DELTA_GIVEN)
112#define SEQ_ENDplusNUMBER (SEQ_END_GIVEN + SEQ_NUMBER_GIVEN)
113#define SEQ_DELTAplusNUMBER (SEQ_DELTA_GIVEN + SEQ_NUMBER_GIVEN)
114
115typedef struct {
116 unsigned long flags;
117 double begin, end, delta;
118 int64_t number, interval;
119} SEQUENCE;
120
121typedef struct {
122 char *columnName;
123 char **item;
124 SEQUENCE *sequence;
125 long items;
126 long sequences;
127 long rows;
128 long repeats;
129 double *data;
130} DEFINITION;
131
132void addSequence(char **item, long items, DEFINITION *definition);
133void addDefinition(char **item, long items, DEFINITION **definition, long *definitions);
134void generateOutput(SDDS_DATASET *outputTable, DEFINITION *definition, long definitions, long doBreak);
135void setupOutputFile(SDDS_DATASET *outputTable, char *output, DEFINITION *definition, long definitions, short columnMajorOrder);
136void createColumn(SDDS_DATASET *outputTable, DEFINITION *definition);
137
138int main(int argc, char **argv) {
139 SDDS_DATASET outputTable;
140 SCANNED_ARG *s_arg;
141 long i_arg, i;
142 DEFINITION *definition = NULL;
143 long definitions = 0;
144 long doBreak = 0;
145
146 char *output = NULL;
147 unsigned long pipeFlags = 0, majorOrderFlag;
148 short columnMajorOrder = 0;
149
151 argc = scanargs(&s_arg, argc, argv);
152 if (argc < 2) {
153 bomb(NULL, USAGE);
154 }
155
156 for (i_arg = 1; i_arg < argc; i_arg++) {
157 if (s_arg[i_arg].arg_type == OPTION) {
158 switch (match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
159 case SET_MAJOR_ORDER:
160 majorOrderFlag = 0;
161 s_arg[i_arg].n_items--;
162 if (s_arg[i_arg].n_items > 0 &&
163 !scanItemList(&majorOrderFlag, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
164 "row", -1, NULL, 0, SDDS_ROW_MAJOR_ORDER,
165 "column", -1, NULL, 0, SDDS_COLUMN_MAJOR_ORDER, NULL)) {
166 SDDS_Bomb("invalid -majorOrder syntax/values");
167 }
168 if (majorOrderFlag & SDDS_COLUMN_MAJOR_ORDER) {
169 columnMajorOrder = 1;
170 } else if (majorOrderFlag & SDDS_ROW_MAJOR_ORDER) {
171 columnMajorOrder = 0;
172 }
173 break;
174
175 case SET_DEFINE:
176 addDefinition(s_arg[i_arg].list + 1, s_arg[i_arg].n_items - 1, &definition, &definitions);
177 break;
178
179 case SET_REPEAT:
180 if (definitions == 0) {
181 SDDS_Bomb("can't give a repeat specifier prior to a definition");
182 }
183 if (s_arg[i_arg].n_items != 2 ||
184 sscanf(s_arg[i_arg].list[1], "%ld", &definition[definitions - 1].repeats) != 1 ||
185 definition[definitions - 1].repeats <= 0) {
186 SDDS_Bomb("invalid -repeat syntax/value");
187 }
188 break;
189
190 case SET_BREAK:
191 doBreak = 1;
192 break;
193
194 case SET_SEQUENCE:
195 if (definitions == 0) {
196 SDDS_Bomb("can't create a sequence prior to defining the variable");
197 }
198 addSequence(s_arg[i_arg].list + 1, s_arg[i_arg].n_items - 1, definition + definitions - 1);
199 break;
200
201 case SET_PIPE:
202 if (!processPipeOption(s_arg[i_arg].list + 1, s_arg[i_arg].n_items - 1, &pipeFlags)) {
203 SDDS_Bomb("invalid -pipe syntax");
204 }
205 break;
206
207 default:
208 fprintf(stderr, "error: unknown/ambiguous option: %s\n", s_arg[i_arg].list[0]);
209 exit(EXIT_FAILURE);
210 break;
211 }
212 } else {
213 if (output == NULL) {
214 output = s_arg[i_arg].list[0];
215 } else {
216 SDDS_Bomb("too many filenames");
217 }
218 }
219 }
220
221 if (output == NULL && !(pipeFlags & USE_STDOUT)) {
222 SDDS_Bomb("no output specified");
223 }
224
225 if (!definitions) {
226 SDDS_Bomb("no sequences defined");
227 }
228
229 setupOutputFile(&outputTable, output, definition, definitions, columnMajorOrder);
230 generateOutput(&outputTable, definition, definitions, doBreak);
231
232 if (!SDDS_Terminate(&outputTable)) {
233 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
234 exit(EXIT_FAILURE);
235 }
236
237 for (i = 0; i < definitions; i++) {
238 free(definition[i].sequence);
239 free(definition[i].data);
240 }
241 free(definition);
242 free_scanargs(&s_arg, argc);
243
244 return EXIT_SUCCESS;
245}
246
247void addDefinition(char **item, long items, DEFINITION **definition, long *definitions) {
248 if (items < 1) {
249 SDDS_Bomb("unable to add definition--supply column name");
250 }
251 *definition = SDDS_Realloc(*definition, sizeof(**definition) * (*definitions + 1));
252 if (!(*definition)) {
253 SDDS_Bomb("unable to add definition---allocation failure");
254 }
255
256 (*definition)[*definitions].columnName = item[0];
257 (*definition)[*definitions].item = item + 1;
258 (*definition)[*definitions].items = items - 1;
259 (*definition)[*definitions].sequence = NULL;
260 (*definition)[*definitions].sequences = 0;
261 (*definition)[*definitions].rows = 0;
262 (*definition)[*definitions].data = NULL;
263 (*definition)[*definitions].repeats = 1;
264 (*definitions)++;
265}
266
267void addSequence(char **item, long items, DEFINITION *definition) {
268 SEQUENCE *sequence;
269 long i;
270
271 definition->sequence = SDDS_Realloc(definition->sequence, sizeof(*definition->sequence) * (definition->sequences + 1));
272 if (!definition->sequence) {
273 SDDS_Bomb("unable to add sequence--memory allocation failure");
274 }
275
276 sequence = &definition->sequence[definition->sequences];
277 sequence->interval = 1;
278
279 if (!scanItemList(&sequence->flags, item, &items, 0,
280 "begin", SDDS_DOUBLE, &sequence->begin, 1, SEQ_BEGIN_GIVEN,
281 "end", SDDS_DOUBLE, &sequence->end, 1, SEQ_END_GIVEN,
282 "number", SDDS_LONG64, &sequence->number, 1, SEQ_NUMBER_GIVEN,
283 "delta", SDDS_DOUBLE, &sequence->delta, 1, SEQ_DELTA_GIVEN,
284 "interval", SDDS_LONG64, &sequence->interval, 1, SEQ_INTERVAL_GIVEN,
285 NULL)) {
286 SDDS_Bomb("invalid -sequence syntax");
287 }
288
289 if ((sequence->flags & SEQ_NUMBER_GIVEN) && sequence->number <= 0) {
290 SDDS_Bomb("number <= 0 is not valid for -sequence");
291 }
292
293 if ((sequence->flags & SEQ_DELTA_GIVEN) && sequence->delta == 0) {
294 SDDS_Bomb("delta == 0 is not valid for -sequence");
295 }
296
297 if (!(sequence->flags & SEQ_BEGIN_GIVEN)) {
298 if (definition->sequences == 0) {
299 SDDS_Bomb("you must give begin point for the first sequence of a definition");
300 }
301 if (!(sequence->flags & SEQ_DELTA_GIVEN)) {
302 SDDS_Bomb("you must give delta with implied begin point");
303 }
304 sequence->begin = definition->sequence[definition->sequences - 1].end + sequence->delta;
305 }
306
307 if ((sequence->flags & SEQ_INTERVAL_GIVEN) && sequence->interval <= 0) {
308 SDDS_Bomb("interval for sequence must be > 0");
309 }
310
311 if ((sequence->flags & SEQ_ENDplusDELTA) == SEQ_ENDplusDELTA) {
312 sequence->number = ((long)((sequence->end - sequence->begin) / sequence->delta + 1.5)) * sequence->interval;
313 if (sequence->number <= 0) {
314 SDDS_Bomb("given (start, end, delta) implies number of points <= 0");
315 }
316 } else if ((sequence->flags & SEQ_ENDplusNUMBER) == SEQ_ENDplusNUMBER) {
317 if (sequence->number == 1) {
318 sequence->delta = 0;
319 } else {
320 sequence->delta = (sequence->end - sequence->begin) / (sequence->number - 1) * sequence->interval;
321 }
322 } else if ((sequence->flags & SEQ_DELTAplusNUMBER) == SEQ_DELTAplusNUMBER) {
323 sequence->end = (sequence->delta / sequence->interval) * (sequence->number - 1) + sequence->begin;
324 } else {
325 SDDS_Bomb("you must supply (end, delta), (end, number), or (delta, number)");
326 }
327
328 definition->data = SDDS_Realloc(definition->data, sizeof(*definition->data) * (definition->rows + sequence->number));
329 if (!definition->data) {
330 SDDS_Bomb("unable to generate sequence data--allocation failure");
331 }
332
333 for (i = 0; i < sequence->number; i++) {
334 definition->data[definition->rows + i] = sequence->begin + (i / sequence->interval) * sequence->delta;
335 }
336
337 definition->rows += sequence->number;
338 definition->sequences++;
339}
340
341void generateOutput(SDDS_DATASET *outputTable, DEFINITION *definition, long definitions, long doBreak) {
342 long idef, row, rows = 0, totalRows = 0;
343
344 if (!doBreak) {
345 for (idef = 0; idef < definitions; idef++) {
346 totalRows = definition[idef].rows * definition[idef].repeats;
347 if (idef && totalRows != rows) {
348 fputs("warning: sequences are of different length (sddssequence)\n", stderr);
349 }
350 if (totalRows > rows) {
351 rows = totalRows;
352 }
353 }
354
355 if (rows == 0) {
356 SDDS_Bomb("total number of points in sequence is zero");
357 }
358
359 if (!SDDS_StartPage(outputTable, rows)) {
360 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
361 }
362
363 for (idef = 0; idef < definitions; idef++) {
364 if (definition[idef].rows != totalRows) {
365 /* repeats are carried out here */
366 definition[idef].data = SDDS_Realloc(definition[idef].data, sizeof(*definition[idef].data) * rows);
367 if (!definition[idef].data) {
368 SDDS_Bomb("unable to generate output--allocation failure");
369 }
370 for (row = definition[idef].rows; row < rows; row++) {
371 definition[idef].data[row] = definition[idef].data[row % definition[idef].rows];
372 }
373 }
374 if (!SDDS_SetColumnFromDoubles(outputTable, SDDS_BY_NAME, definition[idef].data, rows, definition[idef].columnName)) {
375 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
376 }
377 }
378
379 if (!SDDS_WritePage(outputTable)) {
380 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
381 }
382 } else {
383 long irep;
384 rows = definition[0].rows;
385 if (rows == 0) {
386 SDDS_Bomb("number of points in sequence is zero");
387 }
388 for (idef = 1; idef < definitions; idef++) {
389 if (rows != definition[idef].rows) {
390 fputs("warning: sequences are of different length (sddssequence)\n", stderr);
391 }
392 }
393 for (irep = 0; irep < definition[0].repeats; irep++) {
394 if (!SDDS_StartPage(outputTable, rows)) {
395 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
396 }
397 for (idef = 0; idef < definitions; idef++) {
398 if (!SDDS_SetColumnFromDoubles(outputTable, SDDS_BY_NAME, definition[idef].data, rows, definition[idef].columnName)) {
399 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
400 }
401 }
402 if (!SDDS_WritePage(outputTable)) {
403 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
404 }
405 }
406 }
407}
408
409void setupOutputFile(SDDS_DATASET *outputTable, char *output, DEFINITION *definition, long definitions, short columnMajorOrder) {
410 long i;
411
412 if (!SDDS_InitializeOutput(outputTable, SDDS_BINARY, 0, NULL, "sddssequence output", output)) {
413 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
414 }
415
416 for (i = 0; i < definitions; i++) {
417 createColumn(outputTable, &definition[i]);
418 }
419
420 outputTable->layout.data_mode.column_major = columnMajorOrder;
421 if (!SDDS_WriteLayout(outputTable)) {
422 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
423 }
424}
425
426void createColumn(SDDS_DATASET *outputTable, DEFINITION *definition) {
427 char s[SDDS_MAXLINE];
428 char *ptr;
429 long i;
430
431 if (!definition->columnName) {
432 SDDS_Bomb("column name is null");
433 }
434 if (SDDS_GetColumnIndex(outputTable, definition->columnName) >= 0) {
435 SDDS_Bomb("column name already exists (sddssequence)");
436 }
437
438 /* Initialize the column definition string */
439 snprintf(s, sizeof(s), "&column name=%s, ", definition->columnName);
440
441 for (i = 0; i < definition->items; i++) {
442 ptr = strchr(definition->item[i], '=');
443 if (!ptr) {
444 fprintf(stderr, "error: invalid definition-entry: %s\n", definition->item[i]);
445 exit(EXIT_FAILURE);
446 }
447 *ptr = '\0';
448 strcat(s, definition->item[i]);
449 strcat(s, "=\"");
450 strcat(s, ptr + 1);
451 strcat(s, "\", ");
452 }
453
454 /* Ensure type is specified, default to double if not */
455 if (!strstr(s, ", type=")) {
456 strcat(s, " type=\"double\", ");
457 }
458
459 strcat(s, "&end");
460
461 if (!SDDS_ProcessColumnString(outputTable, s, 0)) {
462 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
463 }
464}
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_StartPage(SDDS_DATASET *SDDS_dataset, int64_t expected_n_rows)
int32_t SDDS_SetColumnFromDoubles(SDDS_DATASET *SDDS_dataset, int32_t mode, double *data, int64_t rows,...)
Sets the values for a single data column using double-precision floating-point numbers.
int32_t SDDS_Terminate(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_InitializeOutput(SDDS_DATASET *SDDS_dataset, int32_t data_mode, int32_t lines_per_row, const char *description, const char *contents, const char *filename)
Initializes the SDDS output dataset.
int32_t SDDS_WritePage(SDDS_DATASET *SDDS_dataset)
Writes the current data table to the output file.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
int32_t SDDS_ProcessColumnString(SDDS_DATASET *SDDS_dataset, char *string, int32_t mode)
Process a column definition string.
int32_t SDDS_GetColumnIndex(SDDS_DATASET *SDDS_dataset, char *name)
Retrieves the index of a named column in the SDDS dataset.
void SDDS_PrintErrors(FILE *fp, int32_t mode)
Prints recorded error messages to a specified file stream.
Definition SDDS_utils.c:432
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
Definition SDDS_utils.c:288
void SDDS_Bomb(char *message)
Terminates the program after printing an error message and recorded errors.
Definition SDDS_utils.c:342
void * SDDS_Realloc(void *old_ptr, size_t new_size)
Reallocates memory to a new size.
Definition SDDS_utils.c:677
#define SDDS_DOUBLE
Identifier for the double data type.
Definition SDDStypes.h:37
#define SDDS_LONG64
Identifier for the signed 64-bit integer data type.
Definition SDDStypes.h:49
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
Definition bomb.c:26
long match_string(char *string, char **option, long n_options, long mode)
Matches a given string against an array of option strings based on specified modes.
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
Definition scanargs.c:36
long processPipeOption(char **item, long items, unsigned long *flags)
Definition scanargs.c:356
void free_scanargs(SCANNED_ARG **scanned, int argc)
Definition scanargs.c:584
long scanItemList(unsigned long *flags, char **item, long *items, unsigned long mode,...)
Scans a list of items and assigns values based on provided keywords and types.