SDDS ToolKit Programs and Libraries for C and Python
All Classes Files Functions Variables Macros Pages
mpl2sdds.c
Go to the documentation of this file.
1/**
2 * @file mpl2sdds.c
3 * @brief Converts M. Borland's older `mpl` (Multi-Purpose Library) files into a single SDDS file.
4 *
5 * @details
6 * This program processes multiple `mpl` (Multi-Purpose Library) files and consolidates their data
7 * into a single SDDS (Self Describing Data Set) file. It supports both ASCII and binary formats
8 * for the output SDDS file and provides options to overwrite existing data or create a new file.
9 *
10 * The program ensures consistent formatting of the resulting SDDS file, handling cases such as
11 * differing numbers of rows in input files or existing data conflicts.
12 *
13 * @section Usage
14 * ```
15 * mpl2sdds <mpl-filename> [<mpl-filename>...] -output=<SDDS-filename> [-erase] [-binary]
16 * ```
17 *
18 * @section Options
19 * | Required | Description |
20 * |---------------------------------------|-----------------------------------------------------------------------------|
21 * | `-output` | Specifies the output SDDS file. |
22 *
23 * | Optional | Description |
24 * |---------------------------------------|-----------------------------------------------------------------------------|
25 * | `-erase` | Erase existing data in the output SDDS file before adding new data. |
26 * | `-binary` | Write the SDDS file in binary format instead of ASCII. |
27 *
28 * @subsection Incompatibilities
29 * - `-erase` is incompatible with loading existing data from the specified output file.
30 *
31 * @subsection Requirements
32 * - Input MPL files must have matching numbers of rows.
33 *
34 * @copyright
35 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
36 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
37 *
38 * @license
39 * This file is distributed under the terms of the Software License Agreement
40 * found in the file LICENSE included with this distribution.
41 *
42 * @authors
43 * M. Borland, C. Saunders, R. Soliday
44 */
45
46#include "mdb.h"
47#include "table.h"
48#include "SDDS.h"
49#include "scan.h"
50#include "match_string.h"
51
52/* Enumeration for option types */
53enum option_type {
54 SET_ERASE,
55 SET_OUTPUT,
56 SET_BINARY,
57 N_OPTIONS
58};
59
60char *option[N_OPTIONS] = {
61 "erase",
62 "output",
63 "binary"
64};
65
66/* Improved usage message */
67char *USAGE =
68 "mpl2sdds <mpl-filename> [<mpl-filename>...] \n"
69 " -output=<SDDS-filename>\n"
70 " [-erase] [-binary]\n\n"
71 "Options:\n"
72 " -output=<SDDS-filename> Specifies the output SDDS file. This option is mandatory.\n"
73 " -erase Erase existing data in the output SDDS file before adding new data.\n"
74 " -binary Output the SDDS file in binary format.\n\n"
75 "Program by Michael Borland. (" __DATE__ " " __TIME__ ", SVN revision: " SVN_VERSION ").";
76
77void extract_name_and_unit(char **name, char **unit, char *label);
78long add_definition(SDDS_DATASET *SDDS_dataset, char *label, char *filename);
79void fix_mpl_name(char *name);
80
81int main(int argc, char **argv) {
82 SDDS_DATASET SDDS_dataset;
83 TABLE *mpl_data;
84 SCANNED_ARG *scanned;
85 char **input;
86 long i, j, i_arg, inputs;
87 char *output;
88 long erase, rows, new_columns, SDDS_rows;
89 double **data;
90 long *index;
91 long binary;
92
94 argc = scanargs(&scanned, argc, argv);
95 if (argc < 3) {
96 bomb(NULL, USAGE);
97 }
98
99 input = NULL;
100 output = NULL;
101 inputs = erase = binary = 0;
102
103 for (i_arg = 1; i_arg < argc; i_arg++) {
104 if (scanned[i_arg].arg_type == OPTION) {
105 /* Process options here */
106 switch (match_string(scanned[i_arg].list[0], option, N_OPTIONS, 0)) {
107 case SET_ERASE:
108 erase = 1;
109 break;
110 case SET_OUTPUT:
111 if (scanned[i_arg].n_items != 2) {
112 bomb("Invalid syntax for -output option.", USAGE);
113 }
114 output = scanned[i_arg].list[1];
115 break;
116 case SET_BINARY:
117 if (scanned[i_arg].n_items != 1) {
118 bomb("Invalid syntax for -binary option.", USAGE);
119 }
120 binary = 1;
121 break;
122 default:
123 bomb("Unknown option provided.", USAGE);
124 }
125 } else {
126 input = trealloc(input, (inputs + 1) * sizeof(*input));
127 input[inputs++] = scanned[i_arg].list[0];
128 }
129 }
130
131 if (!output) {
132 bomb("The -output option must be specified.", USAGE);
133 }
134 if (!input) {
135 bomb("No input MPL files provided.", USAGE);
136 }
137
138 if (!erase && fexists(output)) {
139 /* Load data from existing file */
140 if (!SDDS_InitializeInput(&SDDS_dataset, output)) {
141 fprintf(stderr, "Error: Unable to read SDDS layout from %s.\n", output);
142 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
143 return EXIT_FAILURE;
144 }
145 if (!SDDS_ReadPage(&SDDS_dataset)) {
146 fprintf(stderr, "Error: Unable to read data table from %s.\n", output);
147 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
148 return EXIT_FAILURE;
149 }
150 SDDS_rows = SDDS_CountRowsOfInterest(&SDDS_dataset);
151 fclose(SDDS_dataset.layout.fp);
152 SDDS_dataset.layout.fp = fopen_e(output, "w", 0);
153 } else {
154 /* Start a new file */
155 if (!SDDS_InitializeOutput(&SDDS_dataset, binary ? SDDS_BINARY : SDDS_ASCII, 1, NULL, NULL, output)) {
156 fprintf(stderr, "Error: Unable to initialize output SDDS structure for %s.\n", output);
157 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
158 return EXIT_FAILURE;
159 }
160 SDDS_rows = -1;
161 }
162
163 rows = 0;
164 mpl_data = tmalloc(sizeof(*mpl_data) * inputs);
165 data = tmalloc(sizeof(*data) * (2 * inputs));
166 index = tmalloc(sizeof(*index) * (2 * inputs));
167 new_columns = 0;
168
169 for (i = 0; i < inputs; i++) {
170 if (!get_table(&mpl_data[i], input[i], 1, 0)) {
171 fprintf(stderr, "Warning: Unable to read data from %s. Continuing with other files.\n", input[i]);
172 continue;
173 }
174
175 if (!rows) {
176 if (!(rows = mpl_data[i].n_data)) {
177 fprintf(stderr, "Warning: No data in file %s. Continuing with other files.\n", input[i]);
178 continue;
179 }
180 } else if (rows != mpl_data[i].n_data) {
181 SDDS_Bomb("All MPL files must have the same number of data points.");
182 } else if (SDDS_rows != -1 && rows != SDDS_rows) {
183 SDDS_Bomb("Number of data points in MPL files must match the number of rows in the SDDS file.");
184 }
185
186 if ((index[new_columns] = add_definition(&SDDS_dataset, mpl_data[i].xlab, input[i])) < 0) {
187 free(mpl_data[i].c1);
188 } else {
189 data[new_columns++] = mpl_data[i].c1;
190 }
191
192 if ((index[new_columns] = add_definition(&SDDS_dataset, mpl_data[i].ylab, input[i])) < 0) {
193 free(mpl_data[i].c2);
194 } else {
195 data[new_columns++] = mpl_data[i].c2;
196 }
197 }
198
199 if (!rows || !new_columns) {
200 SDDS_Bomb("All input files are empty or invalid.");
201 }
202
203 if (!SDDS_WriteLayout(&SDDS_dataset)) {
204 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
205 return EXIT_FAILURE;
206 }
207 if (!SDDS_StartPage(&SDDS_dataset, rows)) {
208 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
209 return EXIT_FAILURE;
210 }
211
212 for (i = 0; i < new_columns; i++) {
213 for (j = 0; j < rows; j++) {
214 SDDS_SetRowValues(&SDDS_dataset, SDDS_SET_BY_INDEX | SDDS_PASS_BY_VALUE, j, index[i], data[i][j], -1);
215 }
216 }
217
218 if (!SDDS_WritePage(&SDDS_dataset)) {
219 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
220 return EXIT_FAILURE;
221 }
222
223 return EXIT_SUCCESS;
224}
225
226long add_definition(SDDS_DATASET *SDDS_dataset, char *label, char *filename) {
227 char *symbol, *name, *unit;
228 long index;
229
230 extract_name_and_unit(&symbol, &unit, label);
231 SDDS_CopyString(&name, symbol);
232 fix_mpl_name(name);
233
234 if (SDDS_GetColumnIndex(SDDS_dataset, name) >= 0) {
235 fprintf(stderr, "Warning: Column name '%s' from file '%s' already exists and will be ignored.\n", name, filename);
236 return -1;
237 }
238
239 if ((index = SDDS_DefineColumn(SDDS_dataset, name, symbol, unit, NULL, NULL, SDDS_DOUBLE, 0)) < 0) {
240 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
241 return -1;
242 }
243
244 return index;
245}
246
247void extract_name_and_unit(char **name, char **unit, char *label) {
248 char *ptr, *uptr;
249
250 if ((uptr = strchr(label, '('))) {
251 *uptr++ = '\0';
252 if ((ptr = strchr(uptr, ')'))) {
253 *ptr = '\0';
254 }
255 SDDS_CopyString(unit, uptr);
256 } else {
257 *unit = NULL;
258 }
259
260 /* Trim trailing spaces from label */
261 ptr = label + strlen(label) - 1;
262 while (ptr != label && *ptr == ' ') {
263 *ptr-- = '\0';
264 }
265 SDDS_CopyString(name, label);
266}
267
268void fix_mpl_name(char *name) {
269 char *ptr, *ptr1;
270
271 ptr = name;
272 while ((ptr = strchr(ptr, '$'))) {
273 switch (*(ptr + 1)) {
274 case 'a':
275 case 'b':
276 case 'n':
277 case 'g':
278 case 'r':
279 case 's':
280 case 'e':
281 strcpy_ss(ptr, ptr + 2);
282 break;
283 default:
284 ptr += 1;
285 break;
286 }
287 }
288
289 ptr = name;
290 while ((ptr = strchr(ptr, ' '))) {
291 ptr1 = ptr;
292 while (*ptr1 == ' ') {
293 ptr1++;
294 }
295 strcpy_ss(ptr, ptr1);
296 }
297}
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_SetRowValues(SDDS_DATASET *SDDS_dataset, int32_t mode, int64_t row,...)
int32_t SDDS_StartPage(SDDS_DATASET *SDDS_dataset, int64_t expected_n_rows)
int64_t SDDS_CountRowsOfInterest(SDDS_DATASET *SDDS_dataset)
Counts the number of rows marked as "of interest" in the current data table.
int32_t SDDS_InitializeInput(SDDS_DATASET *SDDS_dataset, char *filename)
Definition SDDS_input.c:49
int32_t SDDS_ReadPage(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_DefineColumn(SDDS_DATASET *SDDS_dataset, const char *name, const char *symbol, const char *units, const char *description, const char *format_string, int32_t type, int32_t field_length)
Defines a data column within the SDDS dataset.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
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
int32_t SDDS_CopyString(char **target, const char *source)
Copies a source string to a target string with memory allocation.
Definition SDDS_utils.c:856
#define SDDS_DOUBLE
Identifier for the double data type.
Definition SDDStypes.h:37
void * trealloc(void *old_ptr, uint64_t size_of_block)
Reallocates a memory block to a new size.
Definition array.c:181
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
Definition array.c:59
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
Definition bomb.c:26
long fexists(const char *filename)
Checks if a file exists.
Definition fexists.c:27
FILE * fopen_e(char *file, char *open_mode, long mode)
Opens a file with error checking, messages, and aborts.
Definition fopen_e.c:30
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
char * strcpy_ss(char *dest, const char *src)
Safely copies a string, handling memory overlap.
Definition str_copy.c:34
long get_table(TABLE *tab, char *file, int64_t sample_interval, long flags)
Gets a table from a file in DPL format.
Definition table.c:45