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
|
include <error.h> # for EA_WARN
include <fset.h> # to check whether input or output is redirected
include <tbset.h>
# ttranspose -- transpose or flip a table
# This task can be used to transpose a table so that input rows become
# output columns and input columns become output rows. Another option
# is to flip the table horizontally, that is, the first input column is
# the last output column. Finally, the table can be flipped vertically,
# i.e., the first input row is the last output row. Any combination of
# these operations may be performed.
#
# Phil Hodge, 30-Nov-1994 Task created.
# Phil Hodge, 4-Oct-1995 Modify to use tbn instead of fnt.
# Phil Hodge, 12-Jun-1998 Initialize maxwidth using tbcftl in ttr_compare.
# Phil Hodge, 8-Apr-1999 Call tbfpri.
# Phil Hodge, 8-Jun-1999 Set input/output to STDIN/STDOUT if redirected.
procedure ttranspose()
pointer innames # scratch for names of input tables
pointer outnames # scratch for names of output tables
pointer action # scratch for operations to perform
bool verbose # print table names?
#--
pointer sp
pointer ilist, olist # tbn pointers for input & output lists
pointer intable # scratch for name of an input table
pointer outtable # scratch for name of an output table
pointer itp, otp # pointers to input & output table structs
pointer icp, ocp # pointers to arrays of column descriptors
int op[2] # mapping of (columns,rows) from input to output
int dtype # data type of columns
int nelem # number of elements in array
int irows, orows # number of rows in input, output tables
int icols, ocols # number of columns in input, output tables
int i # loop index
int junk
int num_out # number of output table names
int phu_copied # set by tbfpri and ignored
bool transpose # true if table will be transposed, not just flipped
bool to_stdout # true if output is to STDOUT
bool clgetb(), streq()
int fstati()
pointer tbnopen()
int tbnlen(), tbnget()
pointer tbtopn(), tbcnum()
int tbpsta()
errchk ttr_compare, tbfpri, ttr_create, ttr_trans, ttr_flip
string SKIP "Table %s will be skipped:\n"
begin
call smark (sp)
call salloc (innames, SZ_LINE, TY_CHAR)
call salloc (outnames, SZ_LINE, TY_CHAR)
call salloc (intable, SZ_LINE, TY_CHAR)
call salloc (outtable, SZ_LINE, TY_CHAR)
call salloc (action, SZ_LINE, TY_CHAR)
if (fstati (STDIN, F_REDIR) == YES)
call strcpy ("STDIN", Memc[innames], SZ_LINE)
else
call clgstr ("intable", Memc[innames], SZ_LINE)
if (fstati (STDOUT, F_REDIR) == YES)
call strcpy ("STDOUT", Memc[outnames], SZ_LINE)
else
call clgstr ("outtable", Memc[outnames], SZ_LINE)
call clgstr ("action", Memc[action], SZ_LINE)
verbose = clgetb ("verbose")
# Interpret the list of operations.
call ttr_opcode (Memc[action], op, transpose)
ilist = tbnopen (Memc[innames])
olist = tbnopen (Memc[outnames])
to_stdout = false # may be updated below
num_out = tbnlen (olist)
# Get the first output name now, and then rewind the list.
junk = tbnget (olist, Memc[outtable], SZ_LINE)
call tbnrew (olist)
if (tbnlen (ilist) != num_out) {
if (num_out == 1 && streq (Memc[outtable], "STDOUT")) {
# It's OK to have multiple input tables and just one output
# if the latter is STDOUT.
to_stdout = true
} else {
call tbnclose (olist)
call tbnclose (ilist)
call error (1, "Input and output lists not the same length.")
}
}
# Do for each table in the list.
while (tbnget (ilist, Memc[intable], SZ_LINE) != EOF) {
if (num_out > 1)
junk = tbnget (olist, Memc[outtable], SZ_LINE)
# Open input table and get number of rows and columns.
itp = tbtopn (Memc[intable], READ_ONLY, NULL)
irows = tbpsta (itp, TBL_NROWS)
icols = tbpsta (itp, TBL_NCOLS)
call tbtnam (itp, Memc[intable], SZ_LINE) # get full table name
# Allocate space for pointers to column descriptors.
call malloc (icp, icols, TY_POINTER)
# Get column pointers for input table.
do i = 1, icols
Memi[icp+i-1] = tbcnum (itp, i)
iferr {
# Check that data types of columns are all the same.
call ttr_compare (itp, Memi[icp], icols,
transpose, dtype, nelem)
# Create output table.
call tbfpri (Memc[intable], Memc[outtable], phu_copied)
call ttr_create (itp, otp, Memi[icp], ocp, Memc[outtable],
op, transpose, irows, icols, orows, ocols, dtype, nelem)
} then {
call mfree (icp, TY_POINTER)
call tbtclo (itp)
call eprintf (SKIP)
call pargstr (Memc[intable])
call erract (EA_WARN)
next
}
if (verbose) {
call printf ("%s --> %s\n")
call pargstr (Memc[intable])
call pargstr (Memc[outtable])
call flush (STDOUT)
}
# Copy table data.
iferr {
if (transpose) {
call ttr_trans (itp, otp, Memi[icp], Memi[ocp],
irows, icols, orows, ocols, op, dtype, nelem)
} else {
call ttr_flip (itp, otp, Memi[icp], Memi[ocp],
irows, icols, op)
}
} then {
call mfree (ocp, TY_POINTER)
call mfree (icp, TY_POINTER)
call tbtclo (otp)
call tbtclo (itp)
call tbtdel (Memc[outtable])
call eprintf (SKIP)
call pargstr (Memc[intable])
call erract (EA_WARN)
next
}
call mfree (ocp, TY_POINTER)
call mfree (icp, TY_POINTER)
iferr {
call tbtclo (otp)
} then {
call eprintf (SKIP)
call pargstr (Memc[intable])
call erract (EA_WARN)
}
call tbtclo (itp)
}
call tbnclose (olist)
call tbnclose (ilist)
call sfree (sp)
end
procedure ttr_opcode (action, op, transpose)
char action[ARB] # i: list of operations to perform
int op[2] # o: combined operations
bool transpose # o: true if table will be transposed, not just flipped
#--
int i
int prev[2] # previous op
int slen # length of string
int strlen()
begin
slen = strlen (action)
prev[1] = 1 # initial values
prev[2] = 2
do i = 1, slen {
if (action[i] == 't' || action[i] == '/') {
# transpose
op[1] = prev[2]
op[2] = prev[1]
} else if (action[i] == 'h' || action[i] == '-') {
# flip horizontally, i.e. first column <--> last col
op[1] = -prev[1]
op[2] = prev[2]
} else if (action[i] == 'v' || action[i] == '|') {
# flip vertically, i.e. first row <--> last row
op[1] = prev[1]
op[2] = -prev[2]
} else if (action[i] == ',' || action[i] == ' ') {
;
} else {
call error (1, "'action' must use only t, h, v")
}
prev[1] = op[1] # save for next loop
prev[2] = op[2]
}
# After all the operations, will we actually transpose the table,
# or just flip it?
transpose = (abs (op[1]) == 2)
end
# ttr_compare -- compare data types and array lengths
procedure ttr_compare (itp, icp, icols, transpose, dtype, nelem)
pointer itp # i: pointer to input table struct
pointer icp[icols] # i: array of pointers to input column descriptors
int icols # i: number of columns in input table
bool transpose # i: true if table will be transposed, not just flipped
int dtype # o: data type of columns
int nelem # o: length of array stored at each row,column
#--
int dtype2, nelem2 # data type and array length of column to compare
int width # width of a particular column
int maxwidth # max width of column in text table
int i
int tbpsta(), tbcigi()
begin
# Get info about first column so we can compare with other columns.
dtype = tbcigi (icp[1], TBL_COL_DATATYPE)
nelem = tbcigi (icp[1], TBL_COL_LENDATA)
if (dtype == TY_CHAR) { # old style, change it
dtype = -nelem
nelem = 1
}
# We don't need to check column data types if we're not actually
# transposing the table.
if (!transpose)
return
if (tbpsta (itp, TBL_WHTYPE) == TBL_TYPE_TEXT) {
# For a text table, we can permit different input data types
# if we set the output type to text.
call tbcftl (icp[1], maxwidth) # maxwidth updated in loop
do i = 2, icols {
call tbcftl (icp[i], width) # get width of current column
maxwidth = max (maxwidth, width)
dtype2 = tbcigi (icp[i], TBL_COL_DATATYPE)
if (dtype > 0 && dtype != dtype2) {
# They're not the same; change to char data type.
dtype = -maxwidth
}
}
if (dtype < 0)
dtype = -maxwidth
} else { # not a text table
# Compare first column with subsequent columns.
do i = 2, icols {
dtype2 = tbcigi (icp[i], TBL_COL_DATATYPE)
nelem2 = tbcigi (icp[i], TBL_COL_LENDATA)
if (dtype2 == TY_CHAR) {
dtype2 = -nelem2
nelem2 = 1
}
if (dtype < 0) {
# For character columns, allow different lengths for
# input, but change to maximum length for output.
dtype = min (dtype, dtype2) # max absolute value
} else if (dtype != dtype2) {
# Promote real to double, short to int, and bool to
# any other type.
if (dtype == TY_REAL && dtype2 == TY_DOUBLE ||
dtype == TY_DOUBLE && dtype2 == TY_REAL) {
dtype = TY_DOUBLE
} else if (dtype == TY_INT && dtype2 == TY_SHORT ||
dtype == TY_SHORT && dtype2 == TY_INT) {
dtype = TY_INT
} else if (dtype == TY_BOOL) {
dtype = dtype2 # promote to other type
} else if (dtype2 == TY_BOOL) {
; # OK to convert to any type
} else {
call error (1, "columns are not all the same data type")
}
}
if (nelem != nelem2)
call error (1, "column array lengths are not all the same")
}
}
end
# ttr_create -- create output table
# This routine creates the output table, defines output columns,
# and copies header parameters.
#
# Note the following, which can be a bit confusing. In the case that
# the table is to be flipped horizontally but not transposed, columns are
# defined in the output table in the reverse order from the corresponding
# columns in the input table, but the column pointers themselves are stored
# in their arrays in the same order. That is, icp[i] refers to the same
# column as Memi[ocp+i-1], except of course that icp[i] is in the input
# table and Memi[ocp+i-1] is in the output table. "Same column" means
# that the column descriptions and contents are the same, but the column
# number will in general be different; icp[1] is the first column in the
# input table, Memi[ocp] is the last column in the output table, and they
# will have the same name, etc.
procedure ttr_create (itp, otp, icp, ocp, outtable,
op, transpose, irows, icols, orows, ocols, dtype, nelem)
pointer itp # i: pointer to input table struct
pointer otp # o: pointer to output table struct
pointer icp[icols] # i: array of pointers to input column descriptors
pointer ocp # o: pointer to array of pointers to output col descr
char outtable[ARB] # io: name of output table (extension may be appended)
int op[2] # i: operation code
bool transpose # i: true if table will be transposed, not just flipped
int irows # i: number of rows in input table
int icols # i: number of columns in input table
int orows # o: number of rows in output table
int ocols # o: number of columns in output table
int dtype # i: data type of columns
int nelem # i: length of array stored at each row,column
#--
char colname[SZ_COLNAME] # name of current column
char colunits[SZ_COLUNITS] # units for current column
char colfmt[SZ_COLFMT] # print format for current column
int datatype # data type of current column
int lenarray # number of elements for current column
int lenfmt # space required to print column
int colnum # column number
int i # loop index
int i_start, i_end, i_incr # loop limits for index i
int maxpar # space allocated for header parameters
pointer tbtopn()
int tbpsta()
begin
# Allocate space for array of column pointers for output table.
if (transpose) {
orows = icols
ocols = irows
} else { # don't transpose
orows = irows
ocols = icols
}
call malloc (ocp, ocols, TY_POINTER)
# Create output table.
otp = tbtopn (outtable, NEW_FILE, NULL)
if (tbpsta (itp, TBL_WHTYPE) == TBL_TYPE_TEXT)
call tbpset (otp, TBL_WHTYPE, TBL_TYPE_TEXT)
# Set enough space for all header parameters from input.
maxpar = tbpsta (itp, TBL_MAXPAR)
call tbpset (otp, TBL_MAXPAR, maxpar)
# Create output columns.
if (transpose) {
# Assign dummy column names, with null units and default format.
do i = 1, ocols {
call sprintf (colname, SZ_COLNAME, "c%d")
call pargi (i)
call tbcdef (otp, Memi[ocp+i-1],
colname, "", "", dtype, nelem, 1)
}
} else {
# We're not transposing, so retain column names, etc.
if (op[1] > 0) {
# retain order of columns
i_start = 1
i_end = icols
i_incr = 1
} else {
# flip by defining last column first
i_start = icols
i_end = 1
i_incr = -1
}
do i = i_start, i_end, i_incr {
call tbcinf (icp[i], colnum, colname, colunits, colfmt,
datatype, lenarray, lenfmt)
call tbcdef (otp, Memi[ocp+i-1],
colname, colunits, colfmt, datatype, lenarray, 1)
}
}
call tbtcre (otp)
call tbtnam (otp, outtable, SZ_LINE) # get full table name
# Copy all header parameters from input to output.
call tbhcal (itp, otp)
end
|