1
/* Routines for reading from an RT-11 macro library (like SYSMAC.SML) */
4
Copyright (c) 2001, Richard Krehbiel
7
Redistribution and use in source and binary forms, with or without
8
modification, are permitted provided that the following conditions are
11
o Redistributions of source code must retain the above copyright
12
notice, this list of conditions and the following disclaimer.
14
o Redistributions in binary form must reproduce the above copyright
15
notice, this list of conditions and the following disclaimer in the
16
documentation and/or other materials provided with the distribution.
18
o Neither the name of the copyright holder nor the names of its
19
contributors may be used to endorse or promote products derived from
20
this software without specific prior written permission.
22
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
29
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
31
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
32
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
51
#define WORD(cp) ((*(cp) & 0xff) + ((*((cp)+1) & 0xff) << 8))
53
/* BYTEPOS calculates the byte position within the macro libray file.
54
I use this to sort the entries by their start position, in order to
55
be able to calculate the entries' sizes, which isn't actually
56
stored in the directory. */
58
#define BYTEPOS(rec) ((WORD((rec)+4) & 32767) * 512 + (WORD((rec)+6) & 511))
62
/* compare_position is the qsort callback function that compares byte
63
locations within the macro library */
64
static int compare_position(const void *arg1, const void *arg2)
66
const char *c1 = arg1, *c2 = arg2;
68
if(BYTEPOS(c1) < BYTEPOS(c2))
70
if(BYTEPOS(c1) > BYTEPOS(c2))
76
/* trim removes trailing blanks from a string. */
77
static void trim(char *buf)
79
char *cp = buf + strlen(buf);
80
while(--cp >= buf && *cp == ' ')
84
/* mlb_open opens a file which is given to be a macro library. */
85
/* Returns NULL on failure. */
87
MLB *mlb_open(char *name)
89
MLB *mlb = memcheck(malloc(sizeof(MLB)));
96
mlb->directory = NULL;
98
mlb->fp = fopen(name, "rb");
105
buff = memcheck(malloc(044)); /* Size of MLB library header */
107
if(fread(buff, 1, 044, mlb->fp) < 044)
114
if(WORD(buff) != 01001) /* Is this really a macro library? */
116
mlb_close(mlb); /* Nope. */
120
entsize = WORD(buff + 032); /* The size of each macro directory
122
nr_entries = WORD(buff + 036); /* The number of directory entries */
123
start_block = WORD(buff + 034); /* The start RT-11 block of the
126
free(buff); /* Done with that header. */
128
/* Allocate a buffer for the disk directory */
129
buff = memcheck(malloc(nr_entries * entsize));
130
fseek(mlb->fp, start_block * 512, SEEK_SET); /* Go to the directory */
132
/* Read the disk directory */
133
if(fread(buff, entsize, nr_entries, mlb->fp) < nr_entries)
135
mlb_close(mlb); /* Sorry, read error. */
140
/* Shift occupied directory entries to the front of the array
145
for(i = 0, j = nr_entries; i < j; i++)
148
ent1 = buff + (i * entsize);
149
/* Unused entries have 0177777 0177777 for the RAD50 name,
150
which is not legal RAD50. */
151
if(WORD(ent1) == 0177777 &&
152
WORD(ent1 + 2) == 0177777)
155
(ent2 = buff + (j * entsize),
156
WORD(ent2) == 0177777 &&
157
WORD(ent2+2) == 0177777))
160
break; /* All done. */
161
memcpy(ent1, ent2, entsize); /* Move used entry
164
memset(ent2, 0377, entsize); /* Mark entry unused */
168
/* Now i contains the actual number of entries. */
172
/* Sort the array by file position */
174
qsort(buff, i, entsize, compare_position);
176
/* Now, allocate my in-memory directory */
177
mlb->directory = memcheck(malloc(sizeof(MLBENT) * mlb->nentries));
178
memset(mlb->directory, 0, sizeof(MLBENT) * mlb->nentries);
180
/* Build in-memory directory */
181
for(j = 0; j < i; j++)
186
ent = buff + (j * entsize);
188
unrad50(WORD(ent), radname);
189
unrad50(WORD(ent+2), radname+3);
194
mlb->directory[j].label = memcheck(strdup(radname));
195
mlb->directory[j].position = BYTEPOS(ent);
198
mlb->directory[j].length =
199
BYTEPOS(ent + entsize) - BYTEPOS(ent);
205
fseek(mlb->fp, 0, SEEK_END);
206
max = ftell(mlb->fp);
207
/* Look for last non-zero */
211
fseek(mlb->fp, max, SEEK_SET);
213
} while(max > 0 && c == 0);
215
mlb->directory[j].length = max - BYTEPOS(ent);
223
/* Done. Return the struct that represents the opened MLB. */
227
/* mlb_close discards MLB and closes the file. */
228
void mlb_close(MLB *mlb)
235
for(i = 0; i < mlb->nentries; i++)
237
if(mlb->directory[i].label)
238
free(mlb->directory[i].label);
240
free(mlb->directory);
249
/* mlb_entry returns a BUFFER containing the specified entry from the
250
macro library, or NULL if not found. */
252
BUFFER *mlb_entry(MLB *mlb, char *name)
260
for(i = 0; i < mlb->nentries; i++)
262
ent = &mlb->directory[i];
263
if(strcmp(mlb->directory[i].label, name) == 0)
267
if(i >= mlb->nentries)
270
/* Allocate a buffer to hold the text */
272
buffer_resize(buf, ent->length+1); /* Make it large enough */
275
fseek(mlb->fp, ent->position, SEEK_SET);
277
for(i = 0; i < ent->length; i++)
279
c = fgetc(mlb->fp); /* Get macro byte */
280
if(c == '\r' || c == 0) /* If it's a carriage return or 0,
285
*bp++ = 0; /* Store trailing 0 delim */
287
/* Now resize that buffer to the length actually read. */
288
buffer_resize(buf, bp - buf->buffer);
293
/* mlb_extract - walk thru a macro library and store it's contents
294
into files in the current directory.
296
See, I had decided not to bother writing macro library maintenance
297
tools, since the user can call macros directly from the file
298
system. But if you've already got a macro library without the
299
sources, you can use this to extract the entries and maintain them
300
in the file system from thence forward.
303
void mlb_extract(MLB *mlb)
309
for(i = 0; i < mlb->nentries; i++)
312
buf = mlb_entry(mlb, mlb->directory[i].label);
313
sprintf(name, "%s.MAC", mlb->directory[i].label);
314
fp = fopen(name, "w");
315
fwrite(buf->buffer, 1, buf->length, fp);