Home My Page Projects Code Snippets Project Openings SML/NJ
Summary Activity Forums Tracker Lists Tasks Docs Surveys News SCM Files

SCM Repository

[smlnj] Annotation of /sml/trunk/src/runtime/kernel/boot.c
ViewVC logotype

Annotation of /sml/trunk/src/runtime/kernel/boot.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 543 - (view) (download) (as text)

1 : monnier 249 /* boot.c
2 :     *
3 :     * COPYRIGHT (c) 1993 by AT&T Bell Laboratories.
4 :     *
5 :     * This is the bootstrap loader for booting from .bin files.
6 :     */
7 :    
8 :     #include "ml-osdep.h"
9 :     #include <stdio.h>
10 : monnier 418 #include <string.h>
11 :     #include <stdlib.h>
12 : monnier 249 #include "ml-base.h"
13 :     #include "ml-limits.h"
14 :     #include "cache-flush.h"
15 :     #include "bin-file.h"
16 :     #include "ml-objects.h"
17 :     #include "gc.h"
18 :     #include "ml-globals.h"
19 :    
20 :     #ifndef SEEK_SET
21 :     # define SEEK_SET 0
22 :     #endif
23 :    
24 :     /* The persistent ID list is stored in the PervStruct refcell. It has
25 :     * the following ML type:
26 :     *
27 :     * datatype runDynEnv
28 :     * = NILrde
29 :     * | CONSrde of (Word8Vector.vector * Object.object * runDynEnv)
30 :     */
31 :     #define PerIDList (*PTR_MLtoC(ml_val_t, PervStruct))
32 :    
33 :     PVT ml_val_t BinFileList = LIST_nil; /* A list of bin files to load */
34 :    
35 :    
36 :     /* local routines */
37 : blume 537 PVT ml_val_t BuildFileList (ml_state_t *msp, const char *bootlist);
38 :     PVT FILE *OpenBinFile (const char *fname, bool_t isBinary);
39 :     PVT void ReadBinFile (FILE *file, void *buf, int nbytes, const char *fname);
40 :     PVT void LoadBinFile (ml_state_t *msp, char *fname);
41 : monnier 249 PVT void EnterPerID (ml_state_t *msp, pers_id_t *perID, ml_val_t obj);
42 :     PVT ml_val_t LookupPerID (pers_id_t *perID);
43 :     PVT void ShowPerID (char *buf, pers_id_t *perID);
44 :    
45 : monnier 418 # define HEX(c) (isdigit(c) ? (c) - '0' : (c) - 'a' + 10)
46 : monnier 249
47 :     /* BootML:
48 :     *
49 : blume 537 * Boot the system using the items read from the "bootlist" file.
50 : monnier 439 */
51 : blume 537 void BootML (const char *bootlist, heap_params_t *heapParams)
52 : monnier 249 {
53 :     ml_state_t *msp;
54 : blume 537 char fname[MAX_BOOT_PATH_LEN];
55 :     int rts_init = 0;
56 : monnier 249
57 :     msp = AllocMLState (TRUE, heapParams);
58 :    
59 :     #ifdef HEAP_MONITOR
60 :     if (HeapMon_Init(CmdLineArgs, msp->ml_heap) == FAILURE)
61 :     Die("unable to start heap monitor");
62 :     #endif
63 :    
64 :     InitFaultHandlers ();
65 :     AllocGlobals (msp);
66 :    
67 :     /* construct the list of files to be loaded */
68 : blume 537 BinFileList = BuildFileList (msp, bootlist);
69 : monnier 249
70 :     /* boot the system */
71 :     while (BinFileList != LIST_nil) {
72 :     strcpy(fname, STR_MLtoC(LIST_hd(BinFileList)));
73 :     BinFileList = LIST_tl(BinFileList);
74 : blume 537 if (fname[0] == '#')
75 :     if (rts_init)
76 :     Die ("runtime system registered more than once\n");
77 :     else {
78 :     /* register the runtime system under the given pers id */
79 :     pers_id_t pid;
80 :     int i, l = strlen (fname + 1);
81 :     for (i = 0; i < PERID_LEN; i++) {
82 :     int i2 = 2 * i;
83 :     if (i2 + 1 < l) {
84 :     int c1 = fname[i2+1];
85 :     int c2 = fname[i2+2];
86 :     pid.bytes[i] = (HEX(c1) << 4) + HEX(c2);
87 :     }
88 :     }
89 :     Say ("[Registering runtime system as %s]\n", fname+1);
90 :     EnterPerID (msp, &pid, RunTimeCompUnit);
91 :     rts_init = 1; /* make sure we do this only once */
92 :     }
93 :     else
94 :     LoadBinFile (msp, fname);
95 : monnier 249 }
96 :    
97 :     } /* end of BootML */
98 :    
99 :    
100 :     /* BuildFileList:
101 :     *
102 :     * Given the directory path, build a list of the .bin files in the
103 :     * heap.
104 :     */
105 : blume 537 PVT ml_val_t BuildFileList (ml_state_t *msp, const char *bootlist)
106 : monnier 249 {
107 :     FILE *listF;
108 :     ml_val_t fileNames[MAX_NUM_BOOT_FILES];
109 :     int i, j, numFiles;
110 :     char nameBuf[MAX_BOOT_PATH_LEN];
111 :     ml_val_t fileList;
112 :    
113 : blume 543 numFiles = 0;
114 : blume 537 listF = OpenBinFile (bootlist, FALSE);
115 :     if (listF != NULL) {
116 : monnier 249 /* read in the file names, converting them to ML strings. */
117 :     while (fgets (nameBuf, MAX_BOOT_PATH_LEN, listF) != NIL(char *)) {
118 :     j = strlen(nameBuf)-1;
119 :     if (nameBuf[j] == '\n') nameBuf[j] = '\0'; /* remove "\n" */
120 :     if (numFiles < MAX_NUM_BOOT_FILES)
121 :     fileNames[numFiles++] = ML_CString(msp, nameBuf);
122 :     else
123 :     Die ("too many files\n");
124 :     }
125 :     fclose (listF);
126 :     }
127 :    
128 :     /* create the in-heap list */
129 :     for (fileList = LIST_nil, i = numFiles; --i >= 0; ) {
130 :     LIST_cons(msp, fileList, fileNames[i], fileList);
131 :     }
132 :    
133 :     return fileList;
134 :    
135 :     } /* end of BuildFileList */
136 :    
137 :    
138 :     /* OpenBinFile:
139 :     *
140 :     * Open a file in the bin file directory.
141 :     */
142 : blume 537 PVT FILE *OpenBinFile (const char *fname, bool_t isBinary)
143 : monnier 249 {
144 :     FILE *file;
145 :    
146 : blume 537 if ((file = fopen (fname, isBinary ? "rb" : "r")) == NULL)
147 :     Error ("unable to open \"%s\"\n", fname);
148 : monnier 249
149 :     return file;
150 :    
151 :     } /* end of OpenBinFile */
152 :    
153 :     /*
154 :     * BINFILE FORMAT description:
155 :     *
156 :     *************** The following really belongs in the header file ****************
157 :     * Every 4-byte integer field is stored in big-endian format.
158 :     *
159 :     * Start Size Purpose
160 :     * ----BEGIN OF HEADER----
161 :     * 0 16 magic string
162 :     * 16 4 number of import values (importCnt)
163 :     * 20 4 number of exports (exportCnt = currently always 0 or 1)
164 :     * 24 4 size of CM-specific info in bytes (cmInfoSzB)
165 :     * 28 4 size of pickled lambda-expression in bytes (lambdaSzB)
166 :     * 32 4 size of reserved area 1 in bytes (reserved1)
167 :     * 36 4 size of reserved area 2 in bytes (reserved2)
168 :     * 40 4 size of code area in bytes (codeSzB)
169 :     * 44 4 size of pickled environment in bytes (envSzB)
170 :     * 48 i import trees [This area contains pickled import trees --
171 :     * see below. The total number of leaves in these trees is
172 :     * importCnt. The size impSzB of this area depends on the
173 :     * shape of the trees.]
174 :     * i+48 ex export pids [Each export pid occupies 16 bytes. Thus, the
175 :     * size ex of this area is 16*exportCnt (0 or 16).]
176 :     * ex+i+48 cm CM info [Currently a list of pid-pairs.] (cm = cmInfoSzB)
177 :     * ----END OF HEADER----
178 :     * 0 h HEADER (h = 48+cm+ex+i)
179 :     * h l pickle of exported lambda-expr. (l = lambdaSzB)
180 :     * l+h r reserved areas (r = reserved1+reserved2)
181 :     * r+l+h c code area (c = codeSzB) [Structured into several
182 :     * segments -- see below.]
183 :     * c+r+l+h e pickle of static environment (e = envSzB)
184 :     * e+c+r+l+h - END OF BINFILE
185 :     *
186 :     * IMPORT TREE FORMAT description:
187 :     *
188 :     * The import tree area contains a list of (pid * tree) pairs.
189 :     * The pids are stored directly as 16-byte strings.
190 :     * Trees are constructed according to the following ML-datatype:
191 :     * datatype tree = NODE of (int * tree) list
192 :     * Leaves in this tree have the form (NODE []).
193 :     * Trees are written recursively -- (NODE l) is represented by n (= the
194 :     * length of l) followed by n (int * node) subcomponents. Each component
195 :     * consists of the integer selector followed by the corresponding tree.
196 :     *
197 :     * The size of the import tree area is only given implicitly. When reading
198 :     * this area, the reader must count the number of leaves and compare it
199 :     * with importCnt.
200 :     *
201 :     * Integer values in the import tree area (lengths and selectors) are
202 :     * written in "packed" integer format. In particular, this means that
203 :     * Values in the range 0..127 are represented by only 1 byte.
204 :     * Conceptually, the following pickling routine is used:
205 :     *
206 :     * void recur_write_ul (unsigned long l, FILE *file)
207 :     * {
208 :     * if (l != 0) {
209 :     * recur_write_ul (l >> 7, file);
210 :     * putc ((l & 0x7f) | 0x80, file);
211 :     * }
212 :     * }
213 :     *
214 :     * void write_ul (unsigned long l, FILE *file)
215 :     * {
216 :     * recur_write_ul (l >> 7, file);
217 :     * putc (l & 0x7f, file);
218 :     * }
219 :     *
220 :     * CODE AREA FORMAT description:
221 :     *
222 :     * The code area contains multiple code segements. There will be at least
223 :     * two. The very first segment is the "data" segment -- responsible for
224 :     * creating literal constants on the heap. The idea is that code in the
225 :     * data segment will be executed only once at link-time. Thus, it can
226 :     * then be garbage-collected immediatly. (In the future it is possible that
227 :     * the data segment will not contain executable code at all but some form
228 :     * of bytecode that is to be interpreted separately.)
229 :     *
230 :     * In the binfile, each code segment is represented by its size s (in
231 :     * bytes -- written as a 4-byte big-endian integer) followed by s bytes of
232 :     * machine- (or byte-) code. The total length of all code segments
233 :     * (including the bytes spent on representing individual sizes) is codeSzB.
234 :     *
235 :     * LINKING CONVENTIONS:
236 :     *
237 :     * Linking is achieved by executing all code segments in sequential order.
238 :     *
239 :     * The first code segment (i.e., the "data" segment) receives unit as
240 :     * its single argument.
241 :     *
242 :     * The second code segment receives a record as its single argument.
243 :     * This record has (importCnt+1) components. The first importCnt
244 :     * components correspond to the leaves of the import trees. The final
245 :     * component is the result from executing the data segment.
246 :     *
247 :     * All other code segments receive a single argument which is the result
248 :     * of the preceding segment.
249 :     *
250 :     * The result of the last segment represents the exports of the compilation
251 :     * unit. It is to be paired up with the export pid and stored in the
252 :     * dynamic environment. If there is no export pid, then the final result
253 :     * will be thrown away.
254 :     *
255 :     * The import trees are used for constructing the argument record for the
256 :     * second code segment. The pid at the root of each tree is the key for
257 :     * looking up a value in the existing dynamic environment. In general,
258 :     * that value will be a record. The selector fields of the import tree
259 :     * associated with the pid are used to recursively fetch components of that
260 :     * record.
261 :     */
262 :    
263 :     /* ReadBinFile:
264 :     */
265 : blume 537 PVT void ReadBinFile (FILE *file, void *buf, int nbytes, const char *fname)
266 : monnier 249 {
267 :     if (fread(buf, nbytes, 1, file) == -1)
268 : blume 537 Die ("cannot read file \"%s\"", fname);
269 : monnier 249
270 :     } /* end of ReadBinFile */
271 :    
272 :     /* ReadPackedInt32:
273 :     *
274 :     * Read an integer in "packed" format. (Small numbers only require 1 byte.)
275 :     */
276 : blume 537 PVT Int32_t ReadPackedInt32 (FILE *file, const char *fname)
277 : monnier 249 {
278 :     Unsigned32_t n;
279 :     Byte_t c;
280 :    
281 :     n = 0;
282 :     do {
283 : blume 537 ReadBinFile (file, &c, sizeof(c), fname);
284 : monnier 249 n = (n << 7) | (c & 0x7f);
285 :     } while ((c & 0x80) != 0);
286 :    
287 :     return ((Int32_t)n);
288 :    
289 :     } /* end of ReadPackedInt32 */
290 :    
291 :     /* ImportSelection:
292 :     *
293 :     * Select out the interesting bits from the imported object.
294 :     */
295 : blume 537 PVT void ImportSelection (ml_state_t *msp, FILE *file, const char *fname,
296 :     int *importVecPos, ml_val_t tree)
297 : monnier 249 {
298 : blume 537 Int32_t cnt = ReadPackedInt32 (file, fname);
299 : monnier 249 if (cnt == 0) {
300 :     ML_AllocWrite (msp, *importVecPos, tree);
301 :     (*importVecPos)++;
302 :     }
303 :     else {
304 :     while (cnt-- > 0) {
305 : blume 537 Int32_t selector = ReadPackedInt32 (file, fname);
306 :     ImportSelection (msp, file, fname, importVecPos,
307 :     REC_SEL(tree, selector));
308 : monnier 249 }
309 :     }
310 :    
311 :     } /* end of ImportSelection */
312 :    
313 :     /* LoadBinFile:
314 :     */
315 : blume 537 PVT void LoadBinFile (ml_state_t *msp, char *fname)
316 : monnier 249 {
317 :     FILE *file;
318 :     int i, exportSzB, remainingCode, importRecLen;
319 :     bool_t isDataSeg;
320 :     ml_val_t codeObj, importRec, closure, val;
321 :     binfile_hdr_t hdr;
322 :     pers_id_t exportPerID;
323 :     Int32_t thisSzB;
324 : monnier 439 size_t archiveOffset;
325 : monnier 418 char *atptr, *colonptr;
326 :     char *objname = fname;
327 :    
328 : monnier 249
329 : monnier 418 if ((atptr = strchr (fname, '@')) == NULL)
330 : monnier 439 archiveOffset = 0;
331 : monnier 418 else {
332 : monnier 439 if ((colonptr = strchr (atptr + 1, ':')) != NULL) {
333 :     objname = colonptr + 1;
334 :     *colonptr = '\0';
335 :     }
336 : monnier 418 /* not a lot of extensive checking here... */
337 : monnier 439 archiveOffset = strtoul (atptr + 1, NULL, 0);
338 :     *atptr = '\0';
339 : monnier 418 }
340 :    
341 :     Say ("[Loading %s]\n", objname);
342 :    
343 : monnier 249 /* open the file */
344 : blume 537 file = OpenBinFile (fname, TRUE);
345 : monnier 249 if (file == NULL)
346 :     Exit (1);
347 :    
348 : monnier 418 /* if an offset is given (i.e., we are probably dealing with a stable
349 :     * archive), then seek to the beginning of the section that contains
350 : monnier 439 * the binfile
351 :     */
352 :     if (archiveOffset != 0) {
353 :     if (fseek (file, archiveOffset, SEEK_SET) == -1)
354 : blume 537 Die ("cannot seek on archive file \"%s@%ul\"",
355 :     fname, (unsigned long) archiveOffset);
356 : monnier 439 }
357 : monnier 418
358 : monnier 249 /* get the header */
359 : blume 537 ReadBinFile (file, &hdr, sizeof(binfile_hdr_t), fname);
360 : monnier 249
361 :     /* get header byte order right */
362 :     hdr.importCnt = BIGENDIAN_TO_HOST(hdr.importCnt);
363 :     hdr.exportCnt = BIGENDIAN_TO_HOST(hdr.exportCnt);
364 :     hdr.importSzB = BIGENDIAN_TO_HOST(hdr.importSzB);
365 :     hdr.cmInfoSzB = BIGENDIAN_TO_HOST(hdr.cmInfoSzB);
366 :     hdr.lambdaSzB = BIGENDIAN_TO_HOST(hdr.lambdaSzB);
367 :     hdr.reserved1 = BIGENDIAN_TO_HOST(hdr.reserved1);
368 :     hdr.reserved2 = BIGENDIAN_TO_HOST(hdr.reserved2);
369 :     hdr.codeSzB = BIGENDIAN_TO_HOST(hdr.codeSzB);
370 :     hdr.envSzB = BIGENDIAN_TO_HOST(hdr.envSzB);
371 :    
372 :     /* read the import PerIDs, and create the import vector */
373 :     {
374 :     int importVecPos;
375 :    
376 :     importRecLen = hdr.importCnt + 1;
377 :    
378 :     if (NeedGC (msp, REC_SZB(importRecLen)))
379 :     InvokeGCWithRoots (msp, 0, &BinFileList, NIL(ml_val_t *));
380 :    
381 :     ML_AllocWrite (msp, 0, MAKE_DESC(importRecLen, DTAG_record));
382 :     for (importVecPos = 1; importVecPos < importRecLen; ) {
383 :     pers_id_t importPid;
384 : blume 537 ReadBinFile (file, &importPid, sizeof(pers_id_t), fname);
385 :     ImportSelection (msp, file, fname, &importVecPos,
386 :     LookupPerID(&importPid));
387 : monnier 249 }
388 :     ML_AllocWrite(msp, importRecLen, ML_nil);
389 :     importRec = ML_Alloc(msp, importRecLen);
390 :     }
391 :    
392 :     /* read the export PerID */
393 :     if (hdr.exportCnt == 1) {
394 :     exportSzB = sizeof(pers_id_t);
395 : blume 537 ReadBinFile (file, &exportPerID, exportSzB, fname);
396 : monnier 249 }
397 :     else if (hdr.exportCnt != 0)
398 :     Die ("# of export pids is %d (should be 0 or 1)", (int)hdr.exportCnt);
399 :     else
400 :     exportSzB = 0;
401 :    
402 :     /* seek to code section */
403 :     {
404 : monnier 439 long off = archiveOffset
405 : monnier 418 + sizeof(binfile_hdr_t)
406 : monnier 249 + hdr.importSzB
407 :     + exportSzB
408 :     + hdr.cmInfoSzB
409 :     + hdr.lambdaSzB
410 :     + hdr.reserved1 + hdr.reserved2;
411 :    
412 :     if (fseek(file, off, SEEK_SET) == -1)
413 : blume 537 Die ("cannot seek on bin file \"%s\"", fname);
414 : monnier 249 }
415 :    
416 :     /* Read code objects and run them. The first code object will be the
417 :     * data segment. We add a comment string to each code object to mark
418 :     * which bin file it came from. This code should be the same as that
419 :     * in ../c-libs/smlnj-runtime/mkcode.c.
420 :     */
421 :    
422 :     remainingCode = hdr.codeSzB;
423 :    
424 :     /* read the size for the data object */
425 : blume 537 ReadBinFile (file, &thisSzB, sizeof(Int32_t), fname);
426 : monnier 249 thisSzB = BIGENDIAN_TO_HOST(thisSzB);
427 :    
428 :     remainingCode -= thisSzB + sizeof(Int32_t);
429 :     if (remainingCode < 0)
430 : blume 537 Die ("format error (data size mismatch) in bin file \"%s\"", fname);
431 : monnier 249
432 :     if (thisSzB > 0) {
433 :     Byte_t *dataObj = NEW_VEC(Byte_t, thisSzB);
434 :    
435 : blume 537 ReadBinFile (file, dataObj, thisSzB, fname);
436 : monnier 249 SaveCState (msp, &BinFileList, &importRec, NIL(ml_val_t *));
437 :     val = BuildLiterals (msp, dataObj, thisSzB);
438 :     FREE(dataObj);
439 :     RestoreCState (msp, &BinFileList, &importRec, NIL(ml_val_t *));
440 :     }
441 :     else {
442 :     val = ML_unit;
443 :     }
444 :     /* do a functional update of the last element of the importRec. */
445 :     for (i = 0; i < importRecLen; i++)
446 :     ML_AllocWrite(msp, i, PTR_MLtoC(ml_val_t, importRec)[i-1]);
447 :     ML_AllocWrite(msp, importRecLen, val);
448 :     val = ML_Alloc(msp, importRecLen);
449 :     /* do a GC, if necessary */
450 :     if (NeedGC (msp, PERID_LEN+REC_SZB(5)))
451 :     InvokeGCWithRoots (msp, 0, &BinFileList, &val, NIL(ml_val_t *));
452 :    
453 :     while (remainingCode > 0) {
454 :     int strLen, padLen, extraLen;
455 :    
456 :     /* read the size for this code object */
457 : blume 537 ReadBinFile (file, &thisSzB, sizeof(Int32_t), fname);
458 : monnier 249 thisSzB = BIGENDIAN_TO_HOST(thisSzB);
459 :    
460 :     /* We use one byte for the length, so the longest string is 255
461 :     * characters. We need padding so that the code + string +
462 :     * length byte is WORD_SZB bytes. The padding is inserted between
463 :     * the code and the string.
464 :     */
465 : monnier 418 strLen = strlen(objname);
466 : monnier 249 if (strLen > 255)
467 :     strLen = 255;
468 :     extraLen = strLen+1; /* include byte for length */
469 :     padLen = ROUNDUP(thisSzB+extraLen, WORD_SZB) - (thisSzB+extraLen);
470 :     extraLen += padLen;
471 :    
472 :     /* how much more? */
473 :     remainingCode -= thisSzB + sizeof(Int32_t);
474 :     if (remainingCode < 0)
475 : blume 537 Die ("format error (code size mismatch) in bin file \"%s\"", fname);
476 : monnier 249
477 :     /* allocate space and read code object */
478 :     codeObj = ML_AllocCode (msp, thisSzB+extraLen);
479 : blume 537 ReadBinFile (file, PTR_MLtoC(char, codeObj), thisSzB, fname);
480 : monnier 249
481 :     /* tack on the bin-file name as a comment string. */
482 : monnier 418 memcpy (PTR_MLtoC(char, codeObj)+thisSzB+padLen, objname, strLen);
483 : monnier 249 *(PTR_MLtoC(Byte_t, codeObj)+thisSzB+extraLen-1) = (Byte_t)strLen;
484 :    
485 :     FlushICache (PTR_MLtoC(char, codeObj), thisSzB);
486 :    
487 :     /* create closure */
488 :     REC_ALLOC1 (msp, closure, codeObj);
489 :    
490 :     /* apply the closure to the import PerID vector */
491 :     SaveCState (msp, &BinFileList, NIL(ml_val_t *));
492 :     val = ApplyMLFn (msp, closure, val, TRUE);
493 :     RestoreCState (msp, &BinFileList, NIL(ml_val_t *));
494 :    
495 :     /* do a GC, if necessary */
496 :     if (NeedGC (msp, PERID_LEN+REC_SZB(5)))
497 :     InvokeGCWithRoots (msp, 0, &BinFileList, &val, NIL(ml_val_t *));
498 :     }
499 :    
500 :     /* record the resulting exported PerID */
501 :     if (exportSzB != 0)
502 :     EnterPerID (msp, &exportPerID, val);
503 :    
504 :     fclose (file);
505 :    
506 :     } /* end of LoadBinFile */
507 :    
508 :     /* EnterPerID:
509 :     *
510 :     * Enter a PerID/object binding in the heap allocated list of PerIDs.
511 :     */
512 :     PVT void EnterPerID (ml_state_t *msp, pers_id_t *perID, ml_val_t obj)
513 :     {
514 :     ml_val_t mlPerID;
515 :    
516 :     /* Allocate space for the PerID */
517 :     mlPerID = ML_AllocString (msp, PERID_LEN);
518 :     memcpy (STR_MLtoC(mlPerID), (char *)perID, PERID_LEN);
519 :    
520 :     /* Allocate the list element */
521 :     REC_ALLOC3(msp, PerIDList, mlPerID, obj, PerIDList);
522 :    
523 :     }
524 :    
525 :     /* LookupPerID:
526 :     */
527 :     PVT ml_val_t LookupPerID (pers_id_t *perID)
528 :     {
529 :     ml_val_t p, id;
530 :    
531 :     for (p = PerIDList; p != ML_unit; p = REC_SEL(p, 2)) {
532 :     id = REC_SEL(p, 0);
533 :     if (memcmp((char *)perID, STR_MLtoC(id), PERID_LEN) == 0)
534 :     return (REC_SEL(p, 1));
535 :     }
536 :    
537 :     {
538 :     char buf[64];
539 :     ShowPerID (buf, perID);
540 :     Die ("unable to find PerID %s", buf);
541 :     }
542 :    
543 :     } /* end of LookupPerID */
544 :    
545 :    
546 :     /* ShowPerID:
547 :     */
548 :     PVT void ShowPerID (char *buf, pers_id_t *perID)
549 :     {
550 :     char *cp = buf;
551 :     int i;
552 :    
553 :     *cp++ = '[';
554 :     for (i = 0; i < PERID_LEN; i++) {
555 :     sprintf (cp, "%02x", perID->bytes[i]);
556 :     cp += 2;
557 :     }
558 :     *cp++ = ']';
559 :     *cp++ = '\0';
560 :    
561 :     } /* end of ShowPerID */

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0