Arrays and Vectors
In scripts, data often needs to be managed as an array of items. The CADMATIC script language does not implement arrays as an intrinsic feature, like for example C and C++ do. However, there are externals for several data structures, which implement different kinds of data containers for different purposes.
Name |
Type |
Description |
---|---|---|
One-dimensional array |
Can store a value using any data type, including handles to other structures. Uses dynamic size management. |
|
Two-dimensional array (row x column table) |
Can store a cell value using any data type, including handles to other structures. Uses dynamic size management. |
|
Two-dimensional array (table) |
Implements quick sorting of large arrays. Sorting is based on selected key columns and sorting rules. When sorted, cannot add more rows to the table. |
|
A vector of name–value pairs |
All values are managed as string values. CADMATIC uses tag records to implement a simple ASCII markup format with minimal overhead. |
Vectors
Vector Functions implement a one-dimensional array. They provide dynamic memory reallocation—the size of the vector is automatically increased when needed.
Note: Also an older version of a single-dimensional array interface exists: A_ALLOC, A_PUT, A_GET. These variables are used in many older scripts, and they are also used as arguments or return values of some script extern functions. They are not compatible with DM_VECTOR variables. As a general rule, always use DM_VECTOR if you can, as DM_VECTOR provides more features and better interfaces.
Example
In this example, we use DM_VECTOR to read the following data from a file as a list of lines:
V2002 225 1234 -250 15 V4450 1100 -440 -2300 17 V1223 -200 400 -500 11
The example script loads the lines from the file, sorts the list, and prints the sorted content.

/* This script demonstrates how to use vectors to read a list of lines from a file. */ #include include/dmutil.h main() { fname= "C:/ScriptExamples/ExaData1.txt"; fd = open_file(fname); if(fd < 0)return(-1); name_list_h = construct_line_list(); if( ISINT(name_list_h) ){ F_CLOSE_FILE(fd); return(-1); } if( file_to_line_list(fd, name_list_h) < 0 ){ delete_line_list(name_list_h); F_CLOSE_FILE(fd); return(-1); } F_CLOSE_FILE(fd); print_line_list(name_list_h); delete_line_list(name_list_h); return(0); } /* Open file and return a file descriptor to the file. Return on success: int 0, on error: int -1. */ open_file(string fname) { fd = F_OPEN_FILE(fname,"r"); if( fd < 0 ){ U_MESSAGE("Failed to open file: "+fname+" for read!"); return(-1); } return(fd); } /* Initialize the name list. Name list is a handle to an DM_VECTOR structure. Return on success: The handle to an initialized DM_VECTOR, on error: integer 0 */ construct_line_list() { name_list_h = DM_VECTOR_CREATE(); if(ISINT(name_list_h)){ U_MESSAGE("Unexpected and rare error, failed to create a vector" +"Running out of central memory? "); return(0); } return(name_list_h); } /* Free the allocated memory */ delete_line_list(name_list_h) { DM_VECTOR_DELETE(name_list_h); } /* Read from the open file a line and store to DM_VECTOR. Sort the values alphabetically. Return on success: int 0, on error: int -1 */ file_to_line_list(fd, name_list_h) { /* read from the file, fill to the vector */ continue=TRUE; while(continue){ value=""; nrc = F_READ_LINE(fd, value); /* end of file ? */ if(nrc < 0 ){ continue=FALSE; } else{ /* a new element to the end of vector */ DM_VECTOR_PUSH_BACK(name_list_h, value); } } /* sort */ if( DM_VECTOR_SORT( name_list_h, "STRING", DM_ARRAY_SORT_ASCENDING) ){ U_MESSAGE("Failed to sort. Probably all elements are not the right data type?"); return(-1); } return(0); } print_line_list(name_list_h) { sz = DM_VECTOR_SIZE ( name_list_h ); U_MESSAGE( ITOASCII(sz)+" string values in the list:"); U_MESSAGE( "Sorted content of list:" ); for(i=0;i< sz;i=i+1){ val = DM_VECTOR_GET ( name_list_h, i ); U_MESSAGE("Line: "+ITOASCII(i+1)+": <"+val+">"); } }
Running the script outputs the following to the message pane:
Run>Script 4 string values in the list: Sorted content of list: Line: 1: <V1001 123 432 122 12> Line: 2: <V1223 -200 400 -500 11> Line: 3: <V2002 225 1234 -250 15> Line: 4: <V4450 1100 -440 -2300 17>
2D Arrays
2D Array Functions implement a two-dimensional table of rows and columns.
Note: Also an older version of a two-dimensional array interface exists: alloc_2darray, realloc_2darray, free_2darray, put_2darray, get_2darray, nrows_2darray, ncols_2darray. These are actually scripts defined in the include file array.mac. This old interface should not be used in new scripts. If using the DM_2D_ARRAY externs, there is no need to worry about memory reallocation, while the old realloc_2darray is known to suffer from inadequate run-time performance.
Example
In this example, instead of reading data file into a vector as in Vectors, we read the data as row and column values into a 2D array:
V2002 225 1234 -250 15 V4450 1100 -440 -2300 17 V1223 -200 400 -500 11
Here, the column values represent the name of the item, COG X, COG Y, COG Z, and mass. In the file, the items are delimited by the space character.
The example script loads the lines from the file, sorts the list by mass, and prints the sorted content.

/* This script demonstrates how to use 2D arrays to read records of data from a file and process it */ #include include/dmutil.h /* define the column indices on a row */ #define NAME 0 #define COG_X 1 #define COG_Y 2 #define COG_Z 3 #define MASS 4 /*================= */ #define NR_COLS 5 main() { fname= "C:/ScriptExamples/ExaData2.txt"; fd = open_file(fname); if(fd < 0)return(-1); table_h = construct_table(NR_COLS); if( ISINT(table_h) ){ F_CLOSE_FILE(fd); return(-1); } if( file_to_table(fd, table_h) < 0 ){ delete_table(table_h); F_CLOSE_FILE(fd); return(-1); } F_CLOSE_FILE(fd); print_table(table_h); delete_table(table_h); return(0); } /* Open file and return a file descriptor to the file. Return on success: int 0, on error: int -1. */ open_file(string fname) { fd = F_OPEN_FILE(fname,"r"); if( fd < 0 ){ U_MESSAGE("Failed to open file: "+fname+" for read!"); return(-1); } return(fd); } /* Initialize the table. The table is implemented as a DM_2D_ARRAY structure. Return on success: handle to an initialized DA_2D_ARRAY, on error: integer 0 */ construct_table(int nr_columns) { table_h = DM_2D_ARRAY_CREATE(nr_columns); if(ISINT(table_h)){ U_MESSAGE("Unexpected and rare error, failed to create a 2D table." +" Running out of central memory? "); return(0); } return(table_h); } /* Free the allocated memory */ delete_table(handle table_h) { DM_2D_ARRAY_DELETE(table_h); } /* Read from the open file and write to the columns of the 2D array. Sort acc to the mass. Return on success: int 0, on error: int -1 */ file_to_table(int fd, handle table_h) { float cogx, cogy, cogz, mass; /* read from the file, fill to the vector */ continue=TRUE; while(continue){ value=""; nrc = F_READ_LINE(fd, value); /* end of file ? */ if(nrc < 0 ){ continue=FALSE; } else{ /* a new element to the end of vector */ name="";cogx=0.0; cogy=0.0;cogz=0.0; mass=0.0; nr_read_items = S_SCANF(value,"%s %f %f %f %f",name, cogx, cogy, cogz, mass); if( nr_read_items != 5 ){ U_MESSAGE("Conversion error!"); return(-1); } index_to_new_row = DM_2D_ARRAY_PUSH_BACK_ROW( table_h ); DM_2D_ARRAY_SET(table_h, index_to_new_row, NAME, name); DM_2D_ARRAY_SET(table_h, index_to_new_row, COG_X, cogx); DM_2D_ARRAY_SET(table_h, index_to_new_row, COG_Y, cogy); DM_2D_ARRAY_SET(table_h, index_to_new_row, COG_Z, cogz); DM_2D_ARRAY_SET(table_h, index_to_new_row, MASS, mass); } } /* sort acc. to mass */ if( DM_2D_ARRAY_SORT( table_h, MASS, "float", DM_ARRAY_SORT_ASCENDING) ){ U_MESSAGE("Failed to sort. Probably all elements are not the right data type?"); return(-1); } return(0); } print_table(handle table_h) { float cogx, cogy,cogz,mass; string name; nr_rows=DM_2D_ARRAY_SIZE_ROWS(table_h); U_MESSAGE("Nr rows: "+ITOASCII(nr_rows)+", sorted according to mass"); for(i=0;i<nr_rows;i=i+1){ name = DM_2D_ARRAY_GET(table_h, i, NAME); cogx = DM_2D_ARRAY_GET(table_h, i, COG_X); cogy = DM_2D_ARRAY_GET(table_h, i, COG_Y); cogz = DM_2D_ARRAY_GET(table_h, i, COG_Z); mass = DM_2D_ARRAY_GET(table_h, i, MASS); sval=""; S_PRINTF(sval, "Row: %d: name=%s, COGx=%.1f, COGy=%.1f, COGz=%.1f, Mass=%.1f", i, name, cogx, cogy, cogz, mass); U_MESSAGE(sval); } }
Running the script outputs the following to the message pane:
Run>Script Nr rows: 4, sorted according to mass Row: 0: name=V9223, COGx=-1200.0, COGy=4300.0, COGz=-600.0, Mass=5.0 Row: 1: name=V0451, COGx=2100.0, COGy=-1440.0, COGz=-300.0, Mass=8.0 Row: 2: name=V0008, COGx=133.0, COGy=442.0, COGz=522.0, Mass=11.0 Row: 3: name=V0007, COGx=235.0, COGy=4234.0, COGz=-256.0, Mass=18.0
Advanced Example of Using Vectors and 2D Arrays
There are some general rules regarding good programming practices that are also valid in script programming. In shorter scripts following these practices is less important, but any script with several hundreds of lines or more should be made with these rules in mind:
- Use short functions which encapsulate a single logical action.
- Generally avoid very long source code files. Encapsulate a logical module into a single file.
- Avoid using globals.
- Utilize modularity for clarity and code re-use.
Arrays can help you to apply these rules and make your code more understandable and robust.
Example
In this example, as input we have several data files similar to the one that was processed in the examples in Vectors and 2D Arrays.
We need a script that loads the data lines (item name, COG X, COG Y and COG Z, and mass) from the files into a single table. The table needs to be sorted according to mass and then printed. Additionally, the script should list all files from which data was loaded, and it should allow any number of source files to be used.
Modularity should be supported for clarity and for reusing parts of the code. Global variables should be avoided.
The run-time data structure, utilizing Vector Functions and 2D Array Functions, could look like this:
The root is a file list vector with two elements:
- Handle of list of file names.
- Handle of data table.
The size of the file list vector depends on how many files are selected to be loaded by the user at run time (dynamic reallocation).
The data table is a 2D array of five columns:
- item name
- COG X
- COG Y
- COG Z
- mass
The number of rows the table has depends on how many rows will be loaded from the selected input files.
Due to clarity and code re-use we want to divide the code into three script files and one header file.
- Header file defines the data structure
- Main script implements the workflow and calls the other scripts
- Script which loads the data from the files and manages the data structures
- Script which processes the data
The header file, ExaAdvArray.h:

/* The header data structure of FileLoader To be implemented as a DM_VECTOR */ #define FILELIST_VEC_FILE_NAMES 0 /* handle to a DM_VECTOR: list of the file names, which content was read into the data table */ #define FILELIST_VEC_DATA_TBL 1 /* handle to a DM_2D_ARRAY: stores the data, read from the files */ /*===========================*/ #define FILELIST_VEC_NR_ELEMENTS 2 /* The data table column indices. The table is be implemented as a DM_2D_ARRAY structure */ #define DATATBL_2D_ITEM_NAME 0 /* Name of the item */ #define DATATBL_2D_COG_X 1 /* value of COG x */ #define DATATBL_2D_COG_Y 2 /* value of COG y */ #define DATATBL_2D_COG_Z 3 /* value of COG z */ #define DATATBL_2D_MASS 4 /* value of mass */ /*====================== */ #define DATATBL_2D_NR_COLS 5
The main script, ExaAdvArrayMain.mac:

/* FileLoader: ----------- This script demonstrates how to use DM_VECTOR and DM_2D_ARRAY and PM_CALL_SCRIPT to: - apply modular programming - avoid usage of global variables - utilize arrays to implement your own data structures This script is the main entry point calls external script to load data from files and to free allocated memory calls external script to process the data. Notice: it is expected that all ExaAdvArray*.mac files and ExaAdvArray.h -file are located to C:\ScriptExamples directory. */ #include include/dmutil.h #include include/pm.h #include "C:/ScriptExamples/ExaAdvArray.h" get_script_filepaths( string load_sc_file, string process_sc_file) { my_src_dir = "C:/ScriptExamples"; load_sc_file = F_BUILD_PATHNAME(my_src_dir, "ExaAdvArrayLoad.mac"); process_sc_file = F_BUILD_PATHNAME(my_src_dir, "ExaAdvArrayProcess.mac"); } /* The main entry to the functionality: - load a data from a file - process it - free allocated resources */ main() { load_sc_file=""; process_sc_file=""; /* get paths to script files */ get_script_filepaths( load_sc_file, process_sc_file ); /* create the header structure */ flstr_vec_h = PM_CALL_SCRIPT(load_sc_file, "ConstructFileLoader"); if( ISINT(flstr_vec_h) ){ U_MESSAGE("Error in init."); return(-1); } /* get data to the table from files */ st = PM_CALL_SCRIPT(load_sc_file, "LoadData", flstr_vec_h); if( st ){ PM_CALL_SCRIPT(load_sc_file, "DeleteFileLoader", flstr_vec_h); U_MESSAGE("Error in Load."); return(-1); } /* process the data */ st = PM_CALL_SCRIPT(process_sc_file, "ProcessData", flstr_vec_h); if( st ){ PM_CALL_SCRIPT(load_sc_file, "DeleteFileLoader", flstr_vec_h); U_MESSAGE("Error in Process."); return(-1); } /* free allocated resources */ PM_CALL_SCRIPT(load_sc_file, "DeleteFileLoader", flstr_vec_h); return(0); }
The loader script, ExaAdvArrayLoad.mac:

#include include/dmutil.h #include "C:/ScriptExamples/ExaAdvArray.h" /* Initialize the header structure: DM_VECTOR: list of file names DM_2D_ARRAY: data collected from files Return on success: handle to an initialized DM_VECTOR, on error: int 0 */ init_header_structure() { flstr_vec_h = DM_VECTOR_CREATE(FILELIST_VEC_NR_ELEMENTS); names_vec_h = DM_VECTOR_CREATE(); table_h = DM_2D_ARRAY_CREATE(DATATBL_2D_NR_COLS); if( ISINT(flstr_vec_h) | ISINT(names_vec_h) | ISINT(table_h) ){ U_MESSAGE("Unexpected and rare error, failed to create a vector or a 2D table."+ " Running out of central memory ? "); return(0); } DM_VECTOR_SET(flstr_vec_h, FILELIST_VEC_FILE_NAMES, names_vec_h); DM_VECTOR_SET(flstr_vec_h, FILELIST_VEC_DATA_TBL, table_h); return(flstr_vec_h); } /* Open file and return a file descriptor to the file. Return on success: int file descriptor (0 or bigger), on error: int -1 */ open_file(string fname) { fd = F_OPEN_FILE(fname,"r"); if( fd < 0 ){ U_MESSAGE("Failed to open file: "+fname+" for read!"); return(-1); } return(fd); } /* Select files to load contents. Return on success: int 0, on error: int -1 */ select_files(handle filenames_vec_h) { /* In a real application the user would select the files */ fname1 = "C:/ScriptExamples/ExaData1.txt"; fname2 = "C:/ScriptExamples/ExaData2.txt"; DM_VECTOR_PUSH_BACK(filenames_vec_h, fname1); DM_VECTOR_PUSH_BACK(filenames_vec_h, fname2); return(0); } /* Read from files and write to the columns of the data table (2D array). Store the names of read files into the header structure Return on success: int 0, on error: int -1 */ load_files(handle flstr_vec_h) { filenames_vec_h= DM_VECTOR_GET(flstr_vec_h, FILELIST_VEC_FILE_NAMES); /* get the list of files to load */ if( select_files(filenames_vec_h) ){ /* error or the user selected cancel if interactive selection tool to be used */ return(-1); } nr_files = DM_VECTOR_SIZE(filenames_vec_h); dtable_2d_h = DM_VECTOR_GET(flstr_vec_h, FILELIST_VEC_DATA_TBL); /* load the data in each file into the 2D array */ for(i=0;i< nr_files;i=i+1;){ file_name = DM_VECTOR_GET(filenames_vec_h, i); if( load_file(file_name, dtable_2d_h) ){ U_MESSAGEBOX("Failed to read from <"+file_name+">!", U_MB_ERROR, U_MB_OK, 0); return(-1); } } return(0); } /* Load a single file into the 2D array. Return on success: int 0, on error: int -1 */ load_file(string file_path,handle dtable_2d_h) { float cogx, cogy, cogz, mass; fd = open_file(file_path); if(fd < 0){ U_MESSAGEBOX("Failed to open file: <"+file_path+">!", U_MB_ERROR, U_MB_OK, 0); return(-1); } /* read line by line from the file, convert to column values and store to the columns of the 2d array */ continue=TRUE; while(continue){ value=""; nrc = F_READ_LINE(fd, value); /* end of file ? */ if(nrc < 0 ){ continue=FALSE; } else{ /* a new element to the end of vector */ name="";cogx=0.0; cogy=0.0;cogz=0.0; mass=0.0; nr_read_items = S_SCANF(value,"%s %f %f %f %f",name, cogx, cogy, cogz, mass); if( nr_read_items != 5 ){ U_MESSAGEBOX("File<"+file_path+">: data conversion error!", U_MB_ERROR, U_MB_OK, 0); F_CLOSE_FILE(fd); return(-1); } index_to_new_row = DM_2D_ARRAY_PUSH_BACK_ROW( dtable_2d_h ); DM_2D_ARRAY_SET(dtable_2d_h, index_to_new_row, DATATBL_2D_ITEM_NAME, name); DM_2D_ARRAY_SET(dtable_2d_h, index_to_new_row, DATATBL_2D_COG_X, cogx); DM_2D_ARRAY_SET(dtable_2d_h, index_to_new_row, DATATBL_2D_COG_Y, cogy); DM_2D_ARRAY_SET(dtable_2d_h, index_to_new_row, DATATBL_2D_COG_Z, cogz); DM_2D_ARRAY_SET(dtable_2d_h, index_to_new_row, DATATBL_2D_MASS, mass); } } F_CLOSE_FILE(fd); return(0); } /* The external call interfaces: */ /* Initialize the header structure Return on success: handle to the root structure (DM_VECTOR), on error: int 0 (int 0 is the traditional on error return value for functions which should return a handle on success). */ ConstructFileLoader() { flstr_vec_h = init_header_structure(); if( ISINT(flstr_vec_h) ){ return(0); } return(flstr_vec_h); } /* Get data from files into the structure: Return on success: int 0, on error: int -1 */ LoadData(handle flstr_vec_h ) { flstr_vec_h = init_header_structure(); if( ISINT(flstr_vec_h) ){ return(-1); } if( load_files(flstr_vec_h) < 0 ){ return(-1); /* error */ } return(0); } /* Free the header structure, i.e. allocated memory. If you divide your code to script file modules, then it is logical that the module which allocates also de-allocates. Returns: standard. */ DeleteFileLoader(handle flstr_vec_h) { names_vec_h = DM_VECTOR_GET(flstr_vec_h, FILELIST_VEC_FILE_NAMES); dtable_2d_h = DM_VECTOR_GET(flstr_vec_h, FILELIST_VEC_DATA_TBL); DM_VECTOR_DELETE(names_vec_h); DM_2D_ARRAY_DELETE(dtable_2d_h); DM_VECTOR_DELETE(flstr_vec_h); return(0); }
The script which processes the data, ExaAdvArrayProcess.mac:

#include include/dmutil.h #include "C:/ScriptExamples/ExaAdvArray.h" /* Print the table Return: void */ print_table(handle flstr_vec_h) { float cogx, cogy,cogz,mass; string name; U_MESSAGE("\nFiles loaded:"); filenames_vec_h= DM_VECTOR_GET(flstr_vec_h, FILELIST_VEC_FILE_NAMES); nr_files = DM_VECTOR_SIZE(filenames_vec_h); for(i=0;i<nr_files;i=i+1;){ fn = DM_VECTOR_GET(filenames_vec_h, i); U_MESSAGE("File "+ITOASCII(i+1)+": "+fn); } dtable_2d_h = DM_VECTOR_GET(flstr_vec_h, FILELIST_VEC_DATA_TBL); nr_rows=DM_2D_ARRAY_SIZE_ROWS(dtable_2d_h); U_MESSAGE("\nNr rows: "+ITOASCII(nr_rows)+", sorted acc. to mass:"); for(i=0;i<nr_rows;i=i+1){ name = DM_2D_ARRAY_GET(dtable_2d_h, i, DATATBL_2D_ITEM_NAME); cogx = DM_2D_ARRAY_GET(dtable_2d_h, i, DATATBL_2D_COG_X); cogy = DM_2D_ARRAY_GET(dtable_2d_h, i, DATATBL_2D_COG_Y); cogz = DM_2D_ARRAY_GET(dtable_2d_h, i, DATATBL_2D_COG_Z); mass = DM_2D_ARRAY_GET(dtable_2d_h, i, DATATBL_2D_MASS); sval=""; S_PRINTF(sval, "Row: %d: name=%s, COGx=%.1f, COGy=%.1f, COGz=%.1f, Mass=%.1f", i+1, name, cogx, cogy, cogz, mass); U_MESSAGE(sval); } } /* External call interface: */ /* Process the data table: - sort acc. to mass - print it. Return on success: int 0, on error: int -1 */ ProcessData( handle flstr_vec_h ) { dtable_2d_h = DM_VECTOR_GET(flstr_vec_h, FILELIST_VEC_DATA_TBL); /* sort dat table acc. to the column: mass */ if( DM_2D_ARRAY_SORT( dtable_2d_h, DATATBL_2D_MASS, "float", DM_ARRAY_SORT_ASCENDING) ){ U_MESSAGE("Failed to sort. Probably all elements are not the right data type?"); return(-1); } print_table(flstr_vec_h); return(0); }
When the main script ExaAdvArrayMain.mac is run as a script, it writes the following to the message pane:
Run>Script Files loaded: File 1: C:/ScriptExamples/ExaData1.txt File 2: C:/ScriptExamples/ExaData2.txt Nr rows: 8, sorted acc. to mass: Row: 1: name=V9223, COGx=-1200.0, COGy=4300.0, COGz=-600.0, Mass=5.0 Row: 2: name=V0451, COGx=2100.0, COGy=-1440.0, COGz=-300.0, Mass=8.0 Row: 3: name=V1223, COGx=-200.0, COGy=400.0, COGz=-500.0, Mass=11.0 Row: 4: name=V0008, COGx=133.0, COGy=442.0, COGz=522.0, Mass=11.0 Row: 5: name=V1001, COGx=123.0, COGy=432.0, COGz=122.0, Mass=12.0 Row: 6: name=V2002, COGx=225.0, COGy=1234.0, COGz=-250.0, Mass=15.0 Row: 7: name=V4450, COGx=1100.0, COGy=-440.0, COGz=-2300.0, Mass=17.0 Row: 8: name=V0007, COGx=235.0, COGy=4234.0, COGz=-256.0, Mass=18.0
Note: Use PM_CALL_SCRIPT with caution. There is a performance cost involved when using PM_CALL_SCRIPT instead of a local function. You should avoid repeatedly calling PM_CALL_SCRIPT in time-critical for-loops, for example.
Sort Tables
Sort tables were implemented for sorting masses of data records, such as the data table of a Bill of Materials report. The corresponding externs are declared in the include file rpt.h.
The example script below reads from a file lines that list the following data:
item (string), pieces (integer), description (string)
The script loads the data from each line into the sort table structure and then sorts the table according to the "Name" field.

#include include/dmutil.h #include include/dmrpt.h /* This script demonstrates how sort tables can be used to sort data */ main() { /* Open file and init variables */ f = F_OPEN_FILE("./widgetdata","r"); val = ""; itm = ""; pcs = 0; dsc = ""; /* init sort table and specify fields */ rt = R_INIT_TABLE(); R_DEFINE_FIELD(rt, "Name","STRING"); R_DEFINE_FIELD(rt, "Descrnumber","INT"); R_DEFINE_FIELD(rt, "Address","STRING"); itme_map = R_MAP_FIELD(rt, "Name"); pcs_map = R_MAP_FIELD(rt, "Descrnumber"); dsc_map = R_MAP_FIELD(rt, "Address"); /* sort acc to itme */ R_SET_SORTRULE(rt,"Name"); /* scan file and fill the sort table */ while( F_READ_LINE(f, val) > 0) { S_SCANF(val, "%s%d%s",itm,pcs,dsc); R_ENTER_FIELD(rt, itme_map, itm); R_ENTER_FIELD(rt, pcs_map, pcs); R_ENTER_FIELD(rt, dsc_map, dsc); R_NEW_ROW(rt); } /* close file, do the sort */ F_CLOSE_FILE(f); n_entries = R_TABLE_ISBUILT(rt); U_MESSAGE( ITOASCII(n_entries) + " rows" ); /* U_MESSAGE the rows in sorted order */ st = 0; for(i = 0; i < n_entries; i = i + 1; ){ itm = R_GET_FIELD(rt, i, itme_map, st); if(st)U_MESSAGE("ERROR"); pcs = R_GET_FIELD(rt, i, pcs_map, st); if(st)U_MESSAGE("ERROR"); dsc = R_GET_FIELD(rt, i, dsc_map, st); if(st)U_MESSAGE("ERROR"); U_MESSAGE("Item:"+itm+" Pieces:"+ITOASCII(pcs)+ " Descr:"+dsc); } R_FREE_TABLE(rt); }
Running the script outputs the following to the message pane:
MenuItem(Command)*Select and Run
6 rows
Item:a-1 Pieces:2 Descr:Elbow
Item:a-1 Pieces:2 Descr:Elbow
Item:a-2 Pieces:8 Descr:Bend
Item:a-2 Pieces:8 Descr:Bend
Item:a-3 Pieces:16 Descr:Pipe
Item:a-3 Pieces:16 Descr:Pipe
Note: You cannot add, modify or delete items in the table after it has been sorted.
Tag Records
A plain-text representation of a tag record is the standard semicolon record that is used in CADMATIC system administration in general:
tag_name1 value1; tag_name2 value2; ... tag_namen valuen;;
A tag file consists of one or more tag records. The format allows new lines inside records. Empty field (double semicolon ;;) marks the end of the record.
The tag record utilities were originally made for filtering Bill of Materials files (m-files) in Plant Modeller and Piping Isometrics & Spools. The external function set includes utilities to read tag records from files and to write them into files. Thus, tag records are a very handy method to implement reading and writing of data tables from and into files.
One benefit of using tag records is that it supports clear formatting of textual data, without having to worry about white space (space and tab characters).
Using tag records, the same example data that was used in Sort Tables is formatted like this:
itm a-1;pcs 2;dsc Elbow 90;; itm a-2;pcs 8;dsc Bend 45;; itm a-3;pcs 16;dsc Pipe 60.3 x 5.0;; itm a-1;pcs 2;dsc Elbow 90;; itm a-2;pcs 8;dsc Bend 45;; itm a-3;pcs 16;dsc Pipe 60.3 x 5.0;;
In the script, we can also compute the total number of the "a-1" items and display the resulting value in the output.

#include include/dmutil.h /* This script demonstrates how tag records can be read from a file. */ main() { /* open file for read */ file_descr = F_OPEN_FILE("tagfdata.txt","r"); /* init the container for a tag record */ U_MESSAGE("Item\t\tPieces\t\tDescription"); tagrec_h = DM_INIT_TAGREC(); while( DM_READ_TAGREC(file_descr,tagrec_h) == 3){ /* get the values by name */ itm = DM_GET_TAGVAL(tagrec_h,"itm"); pcs = DM_GET_TAGVAL(tagrec_h,"pcs"); dsc = DM_GET_TAGVAL(tagrec_h, "dsc"); /* NOTE: all values are now STRINGS */ /* print row of values: */ s=""; S_PRINTF(s,"%s\t\t%s\t\t%s", itm, pcs,dsc); U_MESSAGE(s); } F_CLOSE_FILE(file_descr); DM_FREE_TAGREC(tagrec_h); }
Running the script outputs the following to the message pane:
Item Pieces Description a-1 2 Elbow 90 a-2 8 Bend 45 a-3 16 Pipe 60.3 x 5.0 a-1 2 Elbow 90 a-2 8 Bend 45 a-3 16 Pipe 60.3 x 5.0