gb_save.w
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:33k
源码类别:

通讯编程

开发平台:

Visual C++

  1. % This file is part of the Stanford GraphBase (c) Stanford University 1993
  2. @i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES!
  3. @i gb_types.w
  4. deftitle{GB_,SAVE}
  5. prerequisites{GB_,GRAPH}{GB_,IO}
  6. @* Introduction. This GraphBase module contains the code for
  7. two special utility routines, |save_graph| and |restore_graph|, which
  8. convert graphs back and forth between the internal representation that is
  9. described in {sc GB_,GRAPH} and a symbolic file format that is described
  10. below. Researchers can use these routines to transmit graphs between
  11. computers in a machine-independent way, or to use GraphBase graphs with other
  12. graph manipulation software that supports the same symbolic format.
  13. All kinds of tricks are possible in the CEE/ language, so it is
  14. easy to abuse the GraphBase conventions and to create data structures that
  15. make sense only on a particular machine. But if users follow the
  16. recommended ground rules, |save_graph| will be able to transform their
  17. graphs into files that any other GraphBase installation will be able
  18. to read with |restore_graph|. The graphs created on remote machines will
  19. then be semantically equivalent to the originals.
  20. Restrictions: Strings must contain only standard printable characters, not
  21. including .\ or ." or newline, and must be at most 4095 characters long;
  22. the |g->id| string should be at most 154 characters long. All
  23. pointers to vertices and arcs must be confined to blocks within the
  24. |g->data| area; blocks within |g->aux_data| are not saved or restored.
  25. Storage blocks in |g->data| must be ``pure''; that is,
  26. each block must be entirely
  27. devoted either to |Vertex| records, or to |Arc| records, or to
  28. characters of strings. The |save_graph| procedure places all
  29. |Vertex| records into a single |Vertex| block and
  30. all |Arc| records into a single |Arc| block, preserving the
  31. relative order of the original records where possible; but it does not
  32. preserve the relative order of string data in memory. For example, if
  33. |u->name| and |v->name| point to the same memory location in the saved
  34. graph, they will point to different memory locations (representing equal
  35. strings) in the restored graph. All utility fields must conform to
  36. the conventions of the graph's |util_types| string; the .G option, which
  37. leads to graphs within graphs, is not permitted in that string.
  38. @d MAX_SV_STRING 4095 /* longest strings supported */
  39. @d MAX_SV_ID 154 /* longest |id| supported, is less than |ID_FIELD_SIZE| */
  40. @(gb_save.h@>=
  41. extern long save_graph();
  42. extern Graph *restore_graph();
  43. @ Here is an overview of the CEE/ code, .{gb_save.c}, for this module:
  44. @p
  45. #include "gb_io.h" /* we use the input/output conventions of {sc GB_,IO} */
  46. #include "gb_graph.h"
  47.  /* and, of course, the data structures of {sc GB_,GRAPH} */
  48. @h@#
  49. @<Type declarations@>@;
  50. @<Private variables@>@;
  51. @<Private functions@>@;
  52. @<External functions@>
  53. @* External representation of graphs. The internal representation of
  54. graphs has been described in {sc GB_,GRAPH}. We now need to supplement
  55. that description by devising an alternative format suitable for
  56. human-and-machine-readable files.
  57. The following somewhat contrived example illustrates the simple conventions
  58. that we shall follow:
  59. $$letpar=cr obeylines %
  60. vbox{halign{.{#}hfil
  61. * GraphBase graph (util_types IZAZZZZVZZZZSZ,3V,4A)
  62. "somewhat_contrived_example(3.14159265358979323846264338327\
  63. 9502884197169399375105820974944592307816406286208998628)",1,
  64. 3,"pi"
  65. * Vertices
  66. "look",A0,15,A1
  67. "feel",0,-9,A1
  68. "",0,0,0
  69. * Arcs
  70. V0,A2,3,V1
  71. V1,0,5,0
  72. V1,0,-8,1
  73. 0,0,0,0
  74. * Checksum 271828
  75. }}$$
  76. The first line specifies the 14 characters of |util_types| and the total number
  77. of |Vertex| and |Arc| records; in this case there are 3 vertices and
  78. 4~arcs. The next line or lines specify the |id|,
  79. |n|, and |m| fields of the |Graph| record, together with any utility
  80. fields that are not being ignored. In this case, the |id| is a rather
  81. long string; a string may be broken into parts by ending the initial parts
  82. with a backslash, so that no line of the file has more than 79 characters.
  83. The last six characters of |util_types| refer to the utility fields of the
  84. |Graph| record, and in this case they are .{ZZZZSZ}; so all utility
  85. fields are ignored except the second-to-last, |yy|, which is of type
  86. string. The |restore_graph| routine will construct a |Graph| record~|g| from
  87. this example in which |g->n=1|, |g->m=3|, and |g->yy.S="pi"|.
  88. Notice that the individual field values for a record are separated by commas.
  89. If a line ends with a comma, the following line contains
  90. additional fields of the same record.
  91. After the |Graph| record fields have been specified, there's a special line
  92. `.{* Vertices}', after which we learn the fields of each vertex in turn.
  93. First comes the |name| field, then the |arcs| field, and then any
  94. non-ignored utility fields. In this example the |util_types|
  95. for |Vertex| records are .{IZAZZZ}, so the utility field values are
  96. |u.I| and |w.A|. Let |v| point to the first |Vertex| record (which incidentally
  97. is also pointed to by |g->vertices|), and let |a| point to the first
  98. |Arc| record. Then in this example we will have |v->name="look"|,
  99. |v->arcs=a|, |v->u.I=15|, and |v->w.A=(a+1)|.
  100. After the |Vertex| records comes a special line `.{* Arcs}', followed by
  101. the fields of each |Arc| record in an entirely analogous way. First
  102. comes the |tip| field, then the |next| field, then the |len|, and finally
  103. the utility fields (if any). In this example the |util_types|
  104. for |Arc| utility fields are .{ZV}; hence field |a| is ignored, and
  105. field~|b| is a pointer to a |Vertex|. We will have |a->tip=v|, |a->next=(a+2)|,
  106. |a->len=3|, and |a->b.V=(v+1)|.
  107. The null pointer |NULL| is denoted by .0. Furthermore, a |Vertex| pointer
  108. is allowed to have the special value .1, because of conventions
  109. explained in {sc GB_,GATES}. (This special value appears in the fourth
  110. field of the third arc in the example above.) The |restore_graph| procedure
  111. does not allow |Vertex| pointers to take on constant values
  112. greater than~1, nor does it permit the value `.1' where an |Arc|
  113. pointer ought to be.
  114. There should be exactly as many |Vertex| and |Arc| specifications as
  115. indicated after the utility types at the beginning of the file.  The
  116. final |Arc| should then be followed by a special checksum line, which
  117. must contain either a number consistent with the data on all the previous
  118. lines or a negative value (which is not checked).
  119. All information after the checksum line is ignored.
  120. Users should not edit the files produced by |save_graph|, because an
  121. incorrect checksum is liable to ruin everything. However, additional
  122. lines beginning with `.*' may be placed as comments at the very
  123. beginning of the file; such lines are immune to checksumming.
  124. @ We can establish these conventions firmly in mind by writing the
  125. |restore_graph| routine before we write |save_graph|. The subroutine
  126. call |restore_graph("foo.gb")| produces a pointer to the graph
  127. defined in file |"foo.gb"|, or a null pointer in case that file
  128. is unreadable or incorrect. In the latter case, |panic_code|
  129. indicates the problem.
  130. @<External functions@>=
  131. Graph *restore_graph(f)
  132.   char *f; /* the file name */
  133. {@+Graph *g=NULL; /* the graph being restored */
  134.   register char *p; /* register for string manipulation */
  135.   long m; /* the number of |Arc| records to allocate */
  136.   long n; /* the number of |Vertex| records to allocate */
  137.   @<Open the file and parse the first line; |goto sorry| if there's trouble@>;
  138.   @<Create the |Graph| record |g| and fill in its fields@>;
  139.   @<Fill in the fields of all |Vertex| records@>;
  140.   @<Fill in the fields of all |Arc| records@>;
  141.   @<Check the checksum and close the file@>;
  142.   return g;
  143. sorry: gb_raw_close();@+gb_recycle(g);@+return NULL;
  144. }
  145. @ As mentioned above, users can add comment lines at the beginning
  146. of the file, if they put a .* at the beginning of every such line.
  147. But the line that precedes the data proper must adhere to
  148. strict standards.
  149. @d panic(c)@+{@+panic_code=c;@+goto sorry;@+}
  150. @<Open the file...@>=
  151. gb_raw_open(f);
  152. if (io_errors) panic(early_data_fault); /* can't open the file */
  153. while (1) {
  154.   gb_string(str_buf,')');
  155.   if (sscanf(str_buf,"* GraphBase graph (util_types %14[ZIVSA],%ldV,%ldA",
  156.        str_buf+80,&n,&m)==3 && strlen(str_buf+80)==14) break;
  157.   if (str_buf[0]!='*') panic(syntax_error); /* first line is unreadable */
  158. }
  159. @ The previous code has placed the graph's |util_types| into
  160. location |str_buf+80| and verified that it contains precisely
  161. 14 characters, all belonging to the set ${.Z,.I,.V,.S,.A}$.
  162. @<Create the |Graph| record |g| and fill in its fields@>=
  163. g=gb_new_graph(0L);
  164. if (g==NULL) panic(no_room); /* out of memory before we're even started */
  165. gb_free(g->data);
  166. g->vertices=verts=gb_typed_alloc(n==0?1:n,Vertex,g->data);
  167. last_vert=verts+n;
  168. arcs=gb_typed_alloc(m==0?1:m,Arc,g->data);
  169. last_arc=arcs+m;
  170. if (gb_trouble_code) panic(no_room+1);
  171.    /* not enough room for vertices and arcs */
  172. strcpy(g->util_types,str_buf+80);
  173. gb_newline();
  174. if (gb_char()!='"') panic(syntax_error+1);
  175.  /* missing quotes before graph |id| string */
  176. p=gb_string(g->id,'"');
  177. if (*(p-2)=='n' && *(p-3)=='\' && p>g->id+2) {
  178.   gb_newline(); gb_string(p-3,'"');
  179. }
  180. if (gb_char()!='"') panic(syntax_error+2);
  181.  /* missing quotes after graph |id| string */
  182. @<Fill in |g->n|, |g->m|, and |g|'s utility fields@>;
  183. @ The |util_types| and |id| fields are slightly different from other string
  184. fields, because we store them directly in the |Graph| record instead of
  185. storing a pointer. The other fields to be filled by |restore_graph|
  186. can all be done by a macro called |fillin|, which invokes a subroutine
  187. called |fill_field|. The first parameter
  188. to |fillin| is the address of a field in a record; the second parameter
  189. is one of the codes ${.Z,.I,.V,.S,.A}$. A global variable
  190. |comma_expected| is nonzero when this field is not the first in its record.
  191. The value returned by |fill_field| is nonzero if something goes wrong.
  192. We assume here that a utility field takes exactly as much space as
  193. a field of any of its constituent types.
  194. @^system dependencies@>
  195. @d fillin(l,t) if (fill_field((util*)&(l),t)) goto sorry
  196. @<Private f...@>=
  197. static long fill_field(l,t)
  198.   util *l; /* location of field to be filled in */
  199.   char t; /* its type code */
  200. {@+register char c; /* character just read */
  201.   if (t!='Z'&&comma_expected) {
  202.     if (gb_char()!=',') return (panic_code=syntax_error-1); /* missing comma */
  203.     if (gb_char()=='n') gb_newline();
  204.     else gb_backup();
  205.   }
  206.   else comma_expected=1;
  207.   c=gb_char();
  208.   switch (t) {
  209.   case 'I': @<Fill in a numeric field@>;
  210.   case 'V': @<Fill in a vertex pointer@>;
  211.   case 'S': @<Fill in a string pointer@>;
  212.   case 'A': @<Fill in an arc pointer @>;
  213.   default: gb_backup();@+break;
  214.   }
  215.   return panic_code;
  216. }    
  217. @ Some of the communication between |restore_graph| and |fillin| is best
  218. done via global variables.
  219. @<Private v...@>=
  220. static long comma_expected; /* should |fillin| look for a comma? */
  221. static Vertex *verts; /* beginning of the block of |Vertex| records */
  222. static Vertex *last_vert; /* end of the block of |Vertex| records */
  223. static Arc *arcs; /* beginning of the block of |Arc| records */
  224. static Arc *last_arc; /* end of the block of |Arc| records */
  225. @ @<Fill in a numeric field@>=
  226. if (c=='-') l->I=-gb_number(10);
  227. else {
  228.   gb_backup();
  229.   l->I=gb_number(10);
  230. }
  231. break;
  232. @ @<Fill in a vertex pointer@>=
  233. if (c=='V') {
  234.   l->V=verts+gb_number(10);
  235.   if (l->V>=last_vert || l->V<verts)
  236.     panic_code=syntax_error-2; /* vertex address too big */
  237. }@+else if (c=='0' || c=='1') l->I=c-'0';
  238. else panic_code=syntax_error-3; /* vertex numeric address illegal */
  239. break;
  240. @ @<Fill in an arc pointer@>=
  241. if (c=='A') {
  242.   l->A=arcs+gb_number(10);
  243.   if (l->A>=last_arc || l->A<arcs)
  244.     panic_code=syntax_error-4; /* arc address too big */
  245. }@+else if (c=='0') l->A=NULL;
  246. else panic_code=syntax_error-5; /* arc numeric address illegal */
  247. break;
  248. @ We can restore a string slightly longer than the strings we can save.
  249. @<Fill in a string pointer@>=
  250. if (c!='"')
  251.   panic_code=syntax_error-6; /* missing quotes at beginning of string */
  252. else {@+register char* p;
  253.   p=gb_string(item_buf,'"');
  254.   while (*(p-2)=='n' && *(p-3)=='\' && p>item_buf+2 && p<=buffer) {
  255.     gb_newline(); p=gb_string(p-3,'"'); /* splice a broken string together */
  256.   }
  257.   if (gb_char()!='"')
  258.     panic_code=syntax_error-7; /* missing quotes at end of string */
  259.   else if (item_buf[0]=='') l->S=null_string;
  260.   else l->S=gb_save_string(item_buf);
  261. }
  262. break;
  263. @ @d buffer (&item_buf[MAX_SV_STRING+3]) /* the last 81 chars of |item_buf| */
  264. @<Private v...@>=
  265. static char item_buf[MAX_SV_STRING+3+81]; /* an item to be output */
  266. @ When all fields of a record have been filled in, we call |finish_record|
  267. and hope that it returns~0.
  268. @<Private f...@>=
  269. static long finish_record()
  270. {
  271.   if (gb_char()!='n')
  272.     return (panic_code=syntax_error-8); /* garbage present */
  273.   gb_newline();
  274.   comma_expected=0;
  275.   return 0;
  276. }
  277. @ @<Fill in |g->n|, |g->m|, and |g|'s utility fields@>=
  278. panic_code=0;
  279. comma_expected=1;
  280. fillin(g->n,'I');
  281. fillin(g->m,'I');
  282. fillin(g->uu,g->util_types[8]);
  283. fillin(g->vv,g->util_types[9]);
  284. fillin(g->ww,g->util_types[10]);
  285. fillin(g->xx,g->util_types[11]);
  286. fillin(g->yy,g->util_types[12]);
  287. fillin(g->zz,g->util_types[13]);
  288. if (finish_record()) goto sorry;
  289. @ The rest is easy.
  290. @<Fill in the fields of all |Vertex| records@>=
  291. {@+register Vertex* v;
  292.   gb_string(str_buf,'n');
  293.   if (strcmp(str_buf,"* Vertices")!=0)@/
  294.     panic(syntax_error+3); /* introductory line for vertices is missing */
  295.   gb_newline();
  296.   for (v=verts;v<last_vert;v++) {
  297.     fillin(v->name,'S');
  298.     fillin(v->arcs,'A');
  299.     fillin(v->u,g->util_types[0]);
  300.     fillin(v->v,g->util_types[1]);
  301.     fillin(v->w,g->util_types[2]);
  302.     fillin(v->x,g->util_types[3]);
  303.     fillin(v->y,g->util_types[4]);
  304.     fillin(v->z,g->util_types[5]);
  305.     if (finish_record()) goto sorry;
  306.   }
  307. }
  308. @ @<Fill in the fields of all |Arc| records@>=
  309. {@+register Arc* a;
  310.   gb_string(str_buf,'n');
  311.   if (strcmp(str_buf,"* Arcs")!=0)
  312.     panic(syntax_error+4); /* introductory line for arcs is missing */
  313.   gb_newline();
  314.   for (a=arcs;a<last_arc;a++) {
  315.     fillin(a->tip,'V');
  316.     fillin(a->next,'A');
  317.     fillin(a->len,'I');
  318.     fillin(a->a,g->util_types[6]);
  319.     fillin(a->b,g->util_types[7]);
  320.     if (finish_record()) goto sorry;
  321.   }
  322. }
  323. @ @<Check the checksum and close the file@>=
  324. {@+long s;
  325.   gb_string(str_buf,'n');
  326.   if (sscanf(str_buf,"* Checksum %ld",&s)!=1)
  327.     panic(syntax_error+5); /* checksum line is missing */
  328.   if (gb_raw_close()!=s && s>=0)
  329.     panic(late_data_fault); /* checksum does not match */
  330. }
  331. @* Saving a graph. Now that we know how to restore a graph, once it has
  332. been saved, we are ready to write the |save_graph| routine.
  333. Users say |save_graph(g,"foo.gb")|; our job is to create a file
  334. |"foo.gb"| from which the subroutine call |restore_graph("foo.gb")|
  335. will be able to reconstruct a graph equivalent to~|g|, assuming that
  336. |g| meets the restrictions stated earlier.  If nothing goes wrong,
  337. |save_graph| should return the value zero.  Otherwise it should return
  338. an encoded trouble report.
  339. We will set things up so that |save_graph| produces
  340. a syntactically correct file |"foo.gb"| in almost
  341. every case, with explicit error indications written at the end of the file
  342. whenever certain aspects of the given graph have had to be changed.
  343. The value |-1| will be returned if |g==NULL|; the value
  344. |-2| will be returned if |g!=NULL| but the file |"foo.gb"| could not
  345. be opened for output; the value |-3| will be returned if memory is
  346. exhausted. In other cases a file |"foo.gb"| will be created.
  347. Here is a list of things that might go wrong, and the corresponding
  348. corrective actions to be taken in each case, assuming that
  349. |save_graph| does create a file:
  350. @d bad_type_code 0x1 /* illegal character, is changed to |'Z'| */
  351. @d string_too_long 0x2 /* extralong string, is truncated */
  352. @d addr_not_in_data_area 0x4 /* address out of range, is changed to |NULL| */
  353. @d addr_in_mixed_block 0x8 /* address not in pure block, is |NULL|ified */
  354. @d bad_string_char 0x10 /* illegal string character, is changed to |'?'| */
  355. @d ignored_data 0x20 /* nonzero value in |'Z'| format, is not output */ 
  356. @<Private v...@>=
  357. static long anomalies; /* problems accumulated by |save_graph| */
  358. static FILE *save_file; /* the file being written */
  359. @ @<External f...@>=
  360. long save_graph(g,f)
  361.   Graph *g; /* graph to be saved */
  362.   char *f; /* name of the file to be created */
  363. {@+@<Local variables for |save_graph|@>@;@#
  364.   if (g==NULL || g->vertices==NULL) return -1; /* where is |g|? */
  365.   anomalies=0;
  366.   @<Figure out the extent of |g|'s internal records@>;
  367.   save_file=fopen(f,"w");
  368.   if (!save_file) return -2; /* oops, the operating system won't cooperate */
  369.   @<Translate |g| into external format@>;
  370.   @<Make notes at the end of the file about any changes that were necessary@>;
  371.   fclose(save_file);
  372.   gb_free(working_storage);
  373.   return anomalies;
  374. }
  375. @ The main difficulty faced by |save_graph| is the problem of
  376. translating vertex and arc pointers into symbolic form. A graph's
  377. vertices usually appear in a single block, |g->vertices|, but its arcs
  378. usually appear in separate blocks that were created whenever the
  379. |gb_new_arc| routine needed more space. Other blocks, created by
  380. |gb_save_string|, are usually also present in the |g->data| area.  We
  381. need to classify the various data blocks. We also want to be able
  382. to handle graphs that have been created with homegrown methods of
  383. memory allocation, because GraphBase structures need not conform to
  384. the conventions of |gb_new_arc| and |gb_save_string|.
  385. A simple data structure based on &{block_rep} records will
  386. facilitate our task.  Each &{block_rep} will be set up to contain
  387. the information we need to know about a particular block of data
  388. accessible from |g->data|. Such blocks are classified into four
  389. categories, identified by the |cat| field in a &{block_rep}:
  390. @d unk 0 /* |cat| value for blocks of unknown nature */
  391. @d ark 1 /* |cat| value for blocks assumed to hold |Arc| records */
  392. @d vrt 2 /* |cat| value for blocks assumed to hold |Vertex| records */
  393. @d mxt 3 /* |cat| value for blocks being used for more than one purpose */
  394. @<Type...@>=
  395. typedef struct {
  396.   char *start_addr; /* starting address of a data block */
  397.   char *end_addr; /* ending address of a data block */
  398.   long offset; /* index number of first record in the block, if known */
  399.   long cat; /* |cat| code for the block */
  400.   long expl; /* have we finished exploring this block? */
  401. } block_rep;
  402. @ The |block_rep| records don't need to be linked together in any fancy way,
  403. because there usually aren't very many of them. We will simply create
  404. an array, organized in decreasing order of |start_addr| and |end_addr|, with a
  405. dummy record standing as a sentinel at the end.
  406. A system-dependent change might be necessary in the following code,
  407. if pointer values can be longer than 32 bits, or if comparisons between
  408. pointers are undefined.
  409. @^system dependencies@>
  410. @<Private v...@>=
  411. static block_rep* blocks; /* beginning of table of block representatives */
  412. static Area working_storage;
  413. @ Initially we set the |end_addr| field to the location following a
  414. block's data area. Later we will change it as explained below.
  415. The code in this section uses the fact that all bits of storage blocks
  416. are zero until set nonzero. In particular, the |cat| field of each
  417. |block_rep| will initially be |unk|, and the |expl| will be zero;
  418. the |start_addr| and |end_addr| of the sentinel record will be zero.
  419. @<Initialize the |blocks| array@>=
  420. {@+Area t; /* variable that runs through |g->data| */
  421.   for (*t=*(g->data),block_count=0;*t;*t=(*t)->next) block_count++;
  422.   blocks=gb_typed_alloc(block_count+1,block_rep,working_storage);
  423.   if (blocks==NULL) return -3; /* out of memory */
  424.   for (*t=*(g->data),block_count=0;*t;*t=(*t)->next,block_count++) {
  425.     cur_block=blocks+block_count;
  426.     while (cur_block>blocks&&(cur_block-1)->start_addr<(*t)->first) {
  427.       cur_block->start_addr=(cur_block-1)->start_addr;
  428.       cur_block->end_addr=(cur_block-1)->end_addr;
  429.       cur_block--;
  430.     }
  431.     cur_block->start_addr=(*t)->first;
  432.     cur_block->end_addr=(char*)*t;
  433.   }
  434. }
  435. @ @<Local variables for |save...@>=
  436. register block_rep *cur_block; /* the current block of interest */
  437. long block_count; /* how many blocks have we processed? */
  438. @ The |save_graph| routine makes two passes over the graph. The
  439. goal of the first pass is reconnaissance: We try to see where everything
  440. is, and we prune off parts that don't conform to the restrictions.
  441. When we get to the second pass, our task will then be almost trivial.
  442. We will be able to march through the known territory and spew out a copy
  443. of what we encounter. (Items that are ``pruned'' are not actually
  444. removed from |g| itself, only from the portion of~|g| that is saved.)
  445. The first pass is essentially a sequence of calls of the |lookup| macro,
  446. which looks at one field of one record and notes whether
  447. the existence of this field extends the known boundaries of the graph.
  448. The |lookup| macro is a shorthand notation for calling the |classify|
  449. subroutine. We make the same assumption about field sizes as the
  450. |fill_field| routine did above.
  451. @^system dependencies@>
  452. @d lookup(l,t) classify((util*)&(l),t) /* explore field |l| of type |t| */
  453. @<Private f...@>=
  454. static void classify(l,t)
  455.   util *l; /* location of field to be classified */
  456.   char t; /* its type code, from the set ${.Z,.I,.V,.S,.A}$ */
  457. {@+register block_rep *cur_block;
  458.   register char* loc;
  459.   register long tcat; /* category corresponding to |t| */
  460.   register long tsize; /* record size corresponding to |t| */
  461.   switch (t) {
  462.   default: return;
  463.   case 'V': if (l->I==1) return;
  464.     tcat=vrt;
  465.     tsize=sizeof(Vertex);
  466.     break;
  467.   case 'A': tcat=ark;
  468.     tsize=sizeof(Arc);
  469.     break;
  470.   }
  471.   if (l->I==0) return;
  472.   @<Classify a pointer variable@>;
  473. }
  474. @ Here we know that |l| points to a |Vertex| or
  475. to an |Arc|, according as |tcat| is |vrt| or |ark|. We need to check that
  476. this doesn't violate any assumptions about all such pointers lying
  477. in pure blocks within the |g->data| area.
  478. @<Classify a pointer variable@>=
  479. loc=(char*)l->V;
  480. for (cur_block=blocks; cur_block->start_addr>loc; cur_block++) ;
  481. if (loc<cur_block->end_addr) {
  482.   if ((loc-cur_block->start_addr)%tsize!=0 || loc+tsize>cur_block->end_addr)
  483.     cur_block->cat=mxt;
  484.   if (cur_block->cat==unk) cur_block->cat=tcat;
  485.   else if (cur_block->cat!=tcat) cur_block->cat=mxt;
  486. }
  487. @ We go through the list of blocks repeatedly until we reach a stable
  488. situation in which every |vrt| or |ark| block has been explored.
  489. @<Figure out the extent of |g|'s internal records@>=
  490. {@+long activity;
  491.   @<Initialize the |blocks| array@>;
  492.   lookup(g->vertices,'V');
  493.   lookup(g->uu,g->util_types[8]);
  494.   lookup(g->vv,g->util_types[9]);
  495.   lookup(g->ww,g->util_types[10]);
  496.   lookup(g->xx,g->util_types[11]);
  497.   lookup(g->yy,g->util_types[12]);
  498.   lookup(g->zz,g->util_types[13]);
  499.   do@+{@+activity=0;
  500.     for(cur_block=blocks;cur_block->end_addr;cur_block++) {
  501.       if (cur_block->cat==vrt && !cur_block->expl)
  502.         @<Explore a block of supposed vertex records@>@;
  503.       else if (cur_block->cat==ark && !cur_block->expl)
  504.         @<Explore a block of supposed arc records@>@;
  505.       else continue;
  506.       cur_block->expl=activity=1;
  507.    }
  508.   }@+while (activity);
  509. }
  510. @ While we are exploring a block, the |lookup| routine might classify
  511. a previously explored block (or even the current block) as |mxt|.
  512. Therefore some data we assumed would be accessible will actually be
  513. removed from the graph; contradictions that arose might no longer exist.
  514. But we plunge ahead anyway, because we aren't going to try especially
  515. hard to ``save'' portions of graphs that violate our ground rules.
  516. @<Explore a block of supposed vertex records@>=
  517. {@+register Vertex*v;
  518.   for (v=(Vertex*)cur_block->start_addr;@|
  519.       (char*)(v+1)<=cur_block->end_addr && cur_block->cat==vrt;v++) {
  520.     lookup(v->arcs,'A');
  521.     lookup(v->u,g->util_types[0]);
  522.     lookup(v->v,g->util_types[1]);
  523.     lookup(v->w,g->util_types[2]);
  524.     lookup(v->x,g->util_types[3]);
  525.     lookup(v->y,g->util_types[4]);
  526.     lookup(v->z,g->util_types[5]);
  527.   }
  528. }
  529. @ @<Explore a block of supposed arc records@>=
  530. {@+register Arc*a;
  531.   for (a=(Arc*)cur_block->start_addr;@|
  532.       (char*)(a+1)<=cur_block->end_addr && cur_block->cat==ark;a++) {
  533.     lookup(a->tip,'V');
  534.     lookup(a->next,'A');
  535.     lookup(a->a,g->util_types[6]);
  536.     lookup(a->b,g->util_types[7]);
  537.   }
  538. }
  539. @ OK, the first pass is complete. And the second pass is routine:
  540. @<Translate |g| into external format@>=
  541. @<Orient the |blocks| table for translation@>;
  542. @<Initialize the output buffer mechanism and output the first line@>;
  543. @<Translate the |Graph| record@>;
  544. @<Translate the |Vertex| records@>;
  545. @<Translate the |Arc| records@>;
  546. @<Output the checksum line@>;
  547. @ During this pass we decrease the |end_addr| field of a |block_rep|,
  548. so that it points to the first byte of
  549. the final record in a |vrt| or |ark| block.
  550. The variables |m| and |n| are set to the number of arc records and
  551. vertex records, respectively.
  552. @<Local variables for |save...@>=
  553. long m; /* total number of |Arc| records to be translated */
  554. long n; /* total number of |Vertex| records to be translated */
  555. register long s; /* accumulator register for arithmetic calculations */
  556. @ One tricky point needs to be observed, in the unusual case that
  557. there are two or more blocks of &{Vertex} records: The base block
  558. |g->vertices| must come first in the final ordering. (This is the only
  559. exception to the rule that &{Vertex} and &{Arc} records each retain
  560. their relative order with respect to less-than and greater-than.)
  561. @<Orient the |blocks| table for translation@>=
  562. m=0;@+@<Set |n| to the size of the block that starts with |g->vertices|@>;
  563. for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--) {
  564.   if (cur_block->cat==vrt) {
  565.     s=(cur_block->end_addr-cur_block->start_addr)/sizeof(Vertex);
  566.     cur_block->end_addr=cur_block->start_addr+((s-1)*sizeof(Vertex));
  567.     if (cur_block->start_addr!=(char*)g->vertices) {
  568.       cur_block->offset=n;@+ n+=s;
  569.     } /* otherwise |cur_block->offset| remains zero */
  570.   }@+else if (cur_block->cat==ark) {
  571.     s=(cur_block->end_addr-cur_block->start_addr)/sizeof(Arc);
  572.     cur_block->end_addr=cur_block->start_addr+((s-1)*sizeof(Arc));
  573.     cur_block->offset=m;
  574.     m+=s;
  575.   }
  576. }
  577. @ @<Set |n| to the size of the block that starts with |g->vertices|@>=
  578. n=0;
  579. for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--)
  580.   if (cur_block->start_addr==(char *)g->vertices) {
  581.     n=(cur_block->end_addr-cur_block->start_addr)/sizeof(Vertex);
  582.     break;
  583.   }
  584. @ We will store material to be output in the |buffer| array,
  585. so that we can compute the correct checksum.
  586. @<Private v...@>=
  587. static char *buf_ptr; /* the first unfilled position in |buffer| */
  588. static long magic; /* the checksum */
  589. @ @<Private f...@>=
  590. static void flushout() /* output the buffer to |save_file| */
  591. {
  592.   *buf_ptr++='n';
  593.   *buf_ptr='';
  594.   magic=new_checksum(buffer,magic);
  595.   fputs(buffer,save_file);
  596.   buf_ptr=buffer;
  597. }
  598. @ If a supposed string pointer is zero, we output the null string.
  599. (This case arises when a string field has not been initialized,
  600. for example in vertices and arcs that have been allocated but not used.)
  601. @<Private f...@>=
  602. static void prepare_string(s)
  603.   char *s; /* string that is moved to |item_buf| */
  604. {@+register char *p,*q;
  605.   item_buf[0]='"';
  606.   p=&item_buf[1];
  607.   if (s==0) goto sready;
  608.   for (q=s;*q&&p<=&item_buf[MAX_SV_STRING];q++,p++)
  609.     if (*q=='"'||*q=='n'||*q=='\'||imap_ord(*q)==unexpected_char) {
  610.       anomalies |= bad_string_char;
  611.       *p='?';
  612.     }@+else *p=*q;
  613.   if (*q) anomalies |= string_too_long;
  614. sready:  *p='"';
  615.   *(p+1)='';
  616. }
  617. @ The main idea of this part of the program is to format an item into
  618. |item_buf|, then move it to |buffer|, making sure that there is always
  619. room for a comma.
  620. @d append_comma *buf_ptr++=','
  621. @<Private f...@>=
  622. static void move_item()
  623. {@+register long l=strlen(item_buf);
  624.   if (buf_ptr+l>&buffer[78]) {
  625.     if (l<=78) flushout();
  626.     else {@+register char *p=item_buf;
  627.       if (buf_ptr>&buffer[77]) flushout();
  628.            /* no room for initial .{char`"} */
  629.       do@+{
  630.         for (;buf_ptr<&buffer[78];buf_ptr++,p++,l--) *buf_ptr=*p;
  631.         *buf_ptr++='\';
  632.         flushout();
  633.       }@+while(l>78);
  634.     strcpy(buffer,p);
  635.     buf_ptr=&buffer[l];
  636.     return;
  637.     }
  638.   }
  639.   strcpy(buf_ptr,item_buf);
  640.   buf_ptr+=l;
  641. }  
  642.  
  643. @ @<Initialize the output buffer mechanism and output the first line@>=
  644. buf_ptr=buffer;
  645. magic=0;
  646. fputs("* GraphBase graph (util_types ",save_file);
  647. {@+register char*p;
  648.   for (p=g->util_types;p<g->util_types+14;p++)
  649.     if (*p=='Z'||*p=='I'||*p=='V'||*p=='S'||*p=='A') fputc(*p,save_file);
  650.     else fputc('Z',save_file);
  651. }
  652. fprintf(save_file,",%ldV,%ldA)n",n,m);
  653. @ A macro called |trans|, which is sort of an inverse to |fillin|,
  654. takes care of the main work in the second pass.
  655. @d trans(l,t) translate_field((util*)&(l),t)
  656. @<Private f...@>=
  657. static void translate_field(l,t)
  658.   util *l; /* address of field to be output in symbolic form */
  659.   char t; /* type of formatting desired */
  660. {@+register block_rep *cur_block;
  661.   register char* loc;
  662.   register long tcat; /* category corresponding to |t| */
  663.   register long tsize; /* record size corresponding to |t| */
  664.   if (comma_expected) append_comma;
  665.   else comma_expected=1;
  666.   switch (t) {
  667.  default: anomalies|=bad_type_code;
  668.     /* fall through to case .Z */
  669.  case 'Z': buf_ptr--; /* forget spurious comma */
  670.   if (l->I) anomalies|=ignored_data;
  671.   return;
  672.  case 'I': numeric: sprintf(item_buf,"%ld",l->I);@+goto ready;
  673.  case 'S': prepare_string(l->S);@+goto ready;
  674.  case 'V': if (l->I==1) goto numeric;
  675.     tcat=vrt;@+tsize=sizeof(Vertex);@+break;
  676.  case 'A': tcat=ark;@+tsize=sizeof(Arc);@+break;
  677.   }
  678.   @<Translate a pointer variable@>;
  679. ready:move_item();
  680. }
  681. @ @<Translate a pointer variable@>=
  682. loc=(char*)l->V;
  683. item_buf[0]='0';@+item_buf[1]=''; /* |NULL| will be the default */
  684. if (loc==NULL) goto ready;
  685. for (cur_block=blocks; cur_block->start_addr>loc; cur_block++) ;
  686. if (loc>cur_block->end_addr) {
  687.   anomalies|=addr_not_in_data_area;
  688.   goto ready;
  689. }
  690. if (cur_block->cat!=tcat||(loc-cur_block->start_addr)%tsize!=0) {
  691.   anomalies|=addr_in_mixed_block;
  692.   goto ready;
  693. }
  694. sprintf(item_buf,"%c%ld",t,
  695.   cur_block->offset+((loc-cur_block->start_addr)/tsize));
  696. @ @<Translate the |Graph| record@>=
  697. prepare_string(g->id);
  698. if (strlen(g->id)>MAX_SV_ID) {
  699.   strcpy(item_buf+MAX_SV_ID+1,""");
  700.   anomalies|=string_too_long;
  701. }
  702. move_item();
  703. comma_expected=1;
  704. trans(g->n,'I');
  705. trans(g->m,'I');
  706. trans(g->uu,g->util_types[8]);
  707. trans(g->vv,g->util_types[9]);
  708. trans(g->ww,g->util_types[10]);
  709. trans(g->xx,g->util_types[11]);
  710. trans(g->yy,g->util_types[12]);
  711. trans(g->zz,g->util_types[13]);
  712. flushout();
  713. @ @<Translate the |Vertex| records@>=
  714. {@+register Vertex* v;
  715.   fputs("* Verticesn",save_file);
  716.   for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--)
  717.     if (cur_block->cat==vrt && cur_block->offset==0)
  718.       @<Translate all |Vertex| records in |cur_block|@>;
  719.   for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--)
  720.     if (cur_block->cat==vrt && cur_block->offset!=0)
  721.       @<Translate all |Vertex| records in |cur_block|@>;
  722. }
  723. @ @<Translate all |Vertex| records in |cur_block|@>=
  724. for (v=(Vertex*)cur_block->start_addr;
  725.      v<=(Vertex*)cur_block->end_addr;v++) {
  726.   comma_expected=0;
  727.   trans(v->name,'S');
  728.   trans(v->arcs,'A');
  729.   trans(v->u,g->util_types[0]);
  730.   trans(v->v,g->util_types[1]);
  731.   trans(v->w,g->util_types[2]);
  732.   trans(v->x,g->util_types[3]);
  733.   trans(v->y,g->util_types[4]);
  734.   trans(v->z,g->util_types[5]);
  735.   flushout();
  736. }
  737. @ @<Translate the |Arc| records@>=
  738. {@+register Arc* a;
  739.   fputs("* Arcsn",save_file);
  740.   for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--)
  741.     if (cur_block->cat==ark)
  742.       for (a=(Arc*)cur_block->start_addr;a<=(Arc*)cur_block->end_addr;a++) {
  743.         comma_expected=0;
  744.         trans(a->tip,'V');
  745.         trans(a->next,'A');
  746.         trans(a->len,'I');
  747.         trans(a->a,g->util_types[6]);
  748.         trans(a->b,g->util_types[7]);
  749.         flushout();
  750.       }
  751. }
  752. @ @<Output the checksum line@>=
  753. fprintf(save_file,"* Checksum %ldn",magic);
  754. @ @<Make notes at the end of the file about any changes that were necessary@>=
  755. if (anomalies) {
  756.   fputs("> WARNING: I had trouble making this file from the given graph!n",
  757.     save_file);
  758.   if (anomalies&bad_type_code)
  759.     fputs(">> The original util_types had to be corrected.n",save_file);
  760.   if (anomalies&ignored_data)
  761.     fputs(">> Some data suppressed by Z format was actually nonzero.n",
  762.       save_file);
  763.   if (anomalies&string_too_long)
  764.     fputs(">> At least one long string had to be truncated.n",
  765.       save_file);
  766.   if (anomalies&bad_string_char)
  767.     fputs(">> At least one string character had to be changed to '?'.n",
  768.       save_file);
  769.   if (anomalies&addr_not_in_data_area)
  770.     fputs(">> At least one pointer led out of the data area.n",save_file);
  771.   if (anomalies&addr_in_mixed_block)
  772.     fputs(">> At least one data block had an illegal mixture of records.n",
  773.       save_file);
  774.   if (anomalies&(addr_not_in_data_area+addr_in_mixed_block))
  775.     fputs(">>  (Pointers to improper data have been changed to 0.)n",
  776.       save_file);
  777.   fputs("> You should be able to read this file with restore_graph,n",
  778.       save_file);
  779.   fputs("> but the graph you get won't be exactly like the original.n",
  780.       save_file);
  781. }
  782. @* Index. Here is a list that shows where the identifiers of this program are
  783. defined and used.