Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/planarity/graphIO.c
4077 views
1
/*
2
Planarity-Related Graph Algorithms Project
3
Copyright (c) 1997-2010, John M. Boyer
4
All rights reserved. Includes a reference implementation of the following:
5
6
* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding,
7
Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation,
8
University of Victoria, 2001.
9
10
* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n)
11
Planarity by Edge Addition". Journal of Graph Algorithms and Applications,
12
Vol. 8, No. 3, pp. 241-273, 2004.
13
14
* John M. Boyer. "A New Method for Efficiently Generating Planar Graph
15
Visibility Representations". In P. Eades and P. Healy, editors,
16
Proceedings of the 13th International Conference on Graph Drawing 2005,
17
Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.
18
19
Redistribution and use in source and binary forms, with or without modification,
20
are permitted provided that the following conditions are met:
21
22
* Redistributions of source code must retain the above copyright notice, this
23
list of conditions and the following disclaimer.
24
25
* Redistributions in binary form must reproduce the above copyright notice, this
26
list of conditions and the following disclaimer in the documentation and/or
27
other materials provided with the distribution.
28
29
* Neither the name of the Planarity-Related Graph Algorithms Project nor the names
30
of its contributors may be used to endorse or promote products derived from this
31
software without specific prior written permission.
32
33
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
34
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
37
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
40
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
*/
44
45
#include <stdlib.h>
46
#include <string.h>
47
48
#include "graph.h"
49
50
/* Private functions (exported to system) */
51
52
int _ReadAdjMatrix(graphP theGraph, FILE *Infile);
53
int _ReadAdjList(graphP theGraph, FILE *Infile);
54
int _WriteAdjList(graphP theGraph, FILE *Outfile);
55
int _WriteAdjMatrix(graphP theGraph, FILE *Outfile);
56
int _WriteDebugInfo(graphP theGraph, FILE *Outfile);
57
58
/********************************************************************
59
_ReadAdjMatrix()
60
This function reads the undirected graph in upper triangular matrix format.
61
Though O(N^2) time is required, this routine is useful during
62
reliability testing due to the wealth of graph generating software
63
that uses this format for output.
64
Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges
65
********************************************************************/
66
67
int _ReadAdjMatrix(graphP theGraph, FILE *Infile)
68
{
69
int N, I, W, Flag, ErrorCode;
70
71
if (Infile == NULL) return NOTOK;
72
fscanf(Infile, " %d ", &N);
73
if (gp_InitGraph(theGraph, N) != OK)
74
return NOTOK;
75
76
for (I = 0, ErrorCode = OK; I < N-1 && ErrorCode==OK; I++)
77
{
78
theGraph->G[I].v = I;
79
for (W = I+1; W < N; W++)
80
{
81
fscanf(Infile, " %1d", &Flag);
82
if (Flag)
83
{
84
ErrorCode = gp_AddEdge(theGraph, I, 0, W, 0);
85
if (ErrorCode != OK) break;
86
}
87
}
88
}
89
90
return ErrorCode;
91
}
92
93
/********************************************************************
94
_ReadAdjList()
95
This function reads the graph in adjacency list format.
96
97
The file format is
98
On the first line : N= number of vertices
99
On N subsequent lines: #: a b c ... -1
100
where # is a vertex number and a, b, c, ... are its neighbors.
101
102
NOTE: The vertex number is for file documentation only. It is an
103
error if the vertices are not in sorted order in the file.
104
105
NOTE: If a loop edge is found, it is ignored without error.
106
107
NOTE: This routine supports digraphs. For a directed arc (I -> W),
108
an edge record is created in both vertices, I and W, and the
109
edge record in I's adjacency list is marked OUTONLY while the
110
edge record in W's list is marked INONLY.
111
This makes it easy to used edge directedness when appropriate
112
but also seamlessly process the corresponding undirected graph.
113
114
Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges
115
********************************************************************/
116
117
int _ReadAdjList(graphP theGraph, FILE *Infile)
118
{
119
int N, I, W, ErrorCode, adjList, J;
120
121
if (Infile == NULL) return NOTOK;
122
fgetc(Infile); /* Skip the N= */
123
fgetc(Infile);
124
fscanf(Infile, " %d ", &N); /* Read N */
125
if (gp_InitGraph(theGraph, N) != OK)
126
{
127
printf("Failed to init graph");
128
return NOTOK;
129
}
130
131
// Clear the visited members of the vertices so they can be used
132
// during the adjacency list read operation
133
for (I=0; I < N; I++)
134
theGraph->G[I].visited = 0;
135
136
// Do the adjacency list read operation for each vertex in order
137
for (I = 0, ErrorCode = OK; I < N && ErrorCode==OK; I++)
138
{
139
// Read the vertex number
140
fscanf(Infile, "%d", &theGraph->G[I].v);
141
142
// The vertices are expected to be in numeric ascending order
143
if (theGraph->G[I].v != I)
144
return NOTOK;
145
146
// Skip the colon after the vertex number
147
fgetc(Infile);
148
149
// If the vertex already has a non-empty adjacency list, then it is
150
// the result of adding edges during processing of preceding vertices.
151
// The list is removed from the current vertex I and saved for use
152
// during the read operation for I. Adjacencies to preceding vertices
153
// are pulled from this list, if present, or added as directed edges
154
// if not. Adjacencies to succeeding vertices are added as undirected
155
// edges, and will be corrected later if the succeeding vertex does not
156
// have the matching adjacency using the following mechanism. After the
157
// read operation for a vertex I, any adjacency nodes left in the saved
158
// list are converted to directed edges from the preceding vertex to I.
159
adjList = gp_GetFirstArc(theGraph, I);
160
if (gp_IsArc(theGraph, adjList))
161
{
162
// Store the adjacency node location in the visited member of each
163
// of the preceding vertices to which I is adjacent so that we can
164
// efficiently detect the adjacency during the read operation and
165
// efficiently find the adjacency node.
166
J = gp_GetFirstArc(theGraph, I);
167
while (gp_IsArc(theGraph, J))
168
{
169
theGraph->G[theGraph->G[J].v].visited = J;
170
J = gp_GetNextArc(theGraph, J);
171
}
172
173
// Make the adjacency list circular, for later ease of processing
174
gp_SetPrevArc(theGraph, adjList, gp_GetLastArc(theGraph, I));
175
gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, I), adjList);
176
177
// Remove the list from the vertex
178
gp_SetFirstArc(theGraph, I, gp_AdjacencyListEndMark(I));
179
gp_SetLastArc(theGraph, I, gp_AdjacencyListEndMark(I));
180
}
181
182
// Read the adjacency list.
183
while (1)
184
{
185
// Read the next adjacent vertex, with NIL indicating the list end
186
fscanf(Infile, " %d ", &W);
187
if (W < 0) break;
188
189
// Vertex numbers must be less than N
190
if (W >= N)
191
ErrorCode = NOTOK;
192
193
// Loop edges are not supported, but no reason to throw an error if they occur
194
// If a loop occurs, we just do like the ostrich and ignore it
195
else if (W == I)
196
ErrorCode = OK;
197
198
// If the adjacency is to a succeeding, higher numbered vertex,
199
// then we'll add an undirected edge for now
200
else if (I < W)
201
{
202
ErrorCode = gp_AddEdge(theGraph, I, 0, W, 0);
203
}
204
205
// If the adjacency is to a preceding, lower numbered vertex, then
206
// we have to pull the adjacency node from the preexisting adjList,
207
// if it is there, and if not then we have to add a directed edge.
208
else
209
{
210
// If the adjacency node (arc) already exists, then we add it
211
// as the new first arc of the vertex and delete it from adjList
212
if (theGraph->G[W].visited)
213
{
214
J = theGraph->G[W].visited;
215
216
// Remove the arc J from the adjList construct
217
theGraph->G[W].visited = 0;
218
if (adjList == J)
219
{
220
if ((adjList = gp_GetNextArc(theGraph, J)) == J)
221
adjList = NIL;
222
}
223
gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, J), gp_GetPrevArc(theGraph, J));
224
gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, J), gp_GetNextArc(theGraph, J));
225
226
gp_AttachFirstArc(theGraph, I, J);
227
}
228
229
// If an adjacency node to the lower numbered vertex W does not
230
// already exist, then we make a new directed arc from the current
231
// vertex I to W.
232
else
233
{
234
// It is added as the new first arc in both vertices
235
ErrorCode = gp_AddEdge(theGraph, I, 0, W, 0);
236
if (ErrorCode == OK)
237
// Note that this call also sets OUTONLY on the twin arc
238
gp_SetDirection(theGraph, gp_GetFirstArc(theGraph, W), EDGEFLAG_DIRECTION_INONLY);
239
}
240
}
241
242
if (ErrorCode != OK) break;
243
}
244
245
// If there are still adjList entries after the read operation
246
// then those entries are not representative of full undirected edges.
247
// Rather, they represent incoming directed arcs from other vertices
248
// into vertex I. They need to be added back into I's adjacency list but
249
// marked as "INONLY", while the twin is marked "OUTONLY" (by the same function).
250
while (gp_IsArc(theGraph, adjList))
251
{
252
J = adjList;
253
254
theGraph->G[theGraph->G[J].v].visited = 0;
255
256
if ((adjList = gp_GetNextArc(theGraph, J)) == J)
257
adjList = NIL;
258
259
gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, J), gp_GetPrevArc(theGraph, J));
260
gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, J), gp_GetNextArc(theGraph, J));
261
262
gp_AttachFirstArc(theGraph, I, J);
263
gp_SetDirection(theGraph, J, EDGEFLAG_DIRECTION_INONLY);
264
}
265
}
266
267
return ErrorCode;
268
}
269
270
/********************************************************************
271
_ReadLEDAGraph()
272
Reads the edge list from a LEDA file containing a simple undirected graph.
273
********************************************************************/
274
275
int _ReadLEDAGraph(graphP theGraph, FILE *Infile)
276
{
277
char Line[256];
278
int N, I, M, J, u, v;
279
280
/* Skip the lines that say LEDA.GRAPH and give the node and edge types */
281
fgets(Line, 255, Infile);
282
fgets(Line, 255, Infile);
283
fgets(Line, 255, Infile);
284
285
/* Read the number of vertices, then skip that many more lines. */
286
fgets(Line, 255, Infile);
287
sscanf(Line, " %d", &N);
288
for (I = 0; I < N; I++)
289
fgets(Line, 255, Infile);
290
291
/* Initialize the graph */
292
if (gp_InitGraph(theGraph, N) != OK)
293
return NOTOK;
294
295
/* Read the number of edges */
296
fgets(Line, 255, Infile);
297
sscanf(Line, " %d", &M);
298
299
/* Read and add each edge, omitting duplicates */
300
for (J = 0; J < M; J++)
301
{
302
fgets(Line, 255, Infile);
303
sscanf(Line, " %d %d", &u, &v);
304
if (u != v && !gp_IsNeighbor(theGraph, u-1, v-1))
305
{
306
if (gp_AddEdge(theGraph, u-1, 0, v-1, 0) != OK)
307
return NOTOK;
308
}
309
}
310
311
return OK;
312
}
313
314
/********************************************************************
315
gp_Read()
316
Opens the given file, determines whether it is in adjacency list or
317
matrix format based on whether the file start with N or just a number,
318
calls the appropriate read function, then closes the file and returns
319
the graph.
320
321
Digraphs and loop edges are not supported in the adjacency matrix format,
322
which is upper triangular.
323
324
In the adjacency list format, digraphs are supported. Loop edges are
325
ignored without producing an error.
326
327
Pass "stdin" for the FileName to read from the stdin stream
328
329
Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges
330
********************************************************************/
331
332
int gp_Read(graphP theGraph, char *FileName)
333
{
334
FILE *Infile;
335
char Ch;
336
int RetVal;
337
338
if (strcmp(FileName, "stdin") == 0)
339
Infile = stdin;
340
else if ((Infile = fopen(FileName, READTEXT)) == NULL)
341
return NOTOK;
342
343
Ch = (char) fgetc(Infile);
344
ungetc(Ch, Infile);
345
if (Ch == 'N')
346
RetVal = _ReadAdjList(theGraph, Infile);
347
else if (Ch == 'L')
348
RetVal = _ReadLEDAGraph(theGraph, Infile);
349
else RetVal = _ReadAdjMatrix(theGraph, Infile);
350
351
if (RetVal == OK)
352
{
353
void *extraData = NULL;
354
long filePos = ftell(Infile);
355
long fileSize;
356
357
fseek(Infile, 0, SEEK_END);
358
fileSize = ftell(Infile);
359
fseek(Infile, filePos, SEEK_SET);
360
361
if (filePos < fileSize)
362
{
363
extraData = malloc(fileSize - filePos + 1);
364
fread(extraData, fileSize - filePos, 1, Infile);
365
}
366
/*// Useful for quick debugging of IO extensibility
367
if (extraData == NULL)
368
printf("extraData == NULL\n");
369
else
370
{
371
char *extraDataString = (char *) extraData;
372
extraDataString[fileSize - filePos] = '\0';
373
printf("extraData = '%s'\n", extraDataString);
374
}
375
*/
376
377
if (extraData != NULL)
378
{
379
RetVal = theGraph->functions.fpReadPostprocess(theGraph, extraData, fileSize - filePos);
380
free((void *) extraData);
381
}
382
}
383
384
if (strcmp(FileName, "stdin") != 0)
385
fclose(Infile);
386
387
return RetVal;
388
}
389
390
int _ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize)
391
{
392
return OK;
393
}
394
395
/********************************************************************
396
_WriteAdjList()
397
For each vertex, we write its number, a colon, the list of adjacent vertices,
398
then a NIL. The vertices occupy the first N positions of theGraph. Each
399
vertex is also has indicators of the first and last adjacency nodes (arcs)
400
in its adjacency list.
401
402
Returns: NOTOK if either param is NULL; OK otherwise (after printing
403
adjacency list representation to Outfile).
404
********************************************************************/
405
406
int _WriteAdjList(graphP theGraph, FILE *Outfile)
407
{
408
int I, J;
409
410
if (theGraph==NULL || Outfile==NULL) return NOTOK;
411
412
fprintf(Outfile, "N=%d\n", theGraph->N);
413
for (I=0; I < theGraph->N; I++)
414
{
415
fprintf(Outfile, "%d:", I);
416
417
J = gp_GetLastArc(theGraph, I);
418
while (gp_IsArc(theGraph, J))
419
{
420
if (!gp_GetDirection(theGraph, J, EDGEFLAG_DIRECTION_INONLY))
421
fprintf(Outfile, " %d", theGraph->G[J].v);
422
423
J = gp_GetPrevArc(theGraph, J);
424
}
425
fprintf(Outfile, " %d\n", NIL);
426
}
427
return OK;
428
}
429
430
/********************************************************************
431
_WriteAdjMatrix()
432
Outputs upper triangular matrix representation capable of being
433
read by _ReadAdjMatrix()
434
435
Note: This routine does not support digraphs and will return an
436
error if a directed edge is found.
437
438
returns OK for success, NOTOK for failure
439
********************************************************************/
440
441
int _WriteAdjMatrix(graphP theGraph, FILE *Outfile)
442
{
443
int I, J, K;
444
char *Row = NULL;
445
446
if (theGraph != NULL)
447
Row = (char *) malloc((theGraph->N+1)*sizeof(char));
448
449
if (Row==NULL || theGraph==NULL || Outfile==NULL)
450
{
451
if (Row != NULL) free(Row);
452
return NOTOK;
453
}
454
455
fprintf(Outfile, "%d\n", theGraph->N);
456
for (I = 0; I < theGraph->N; I++)
457
{
458
for (K = 0; K <= I; K++)
459
Row[K] = ' ';
460
for (K = I+1; K < theGraph->N; K++)
461
Row[K] = '0';
462
463
J = gp_GetFirstArc(theGraph, I);
464
while (gp_IsArc(theGraph, J))
465
{
466
if (gp_GetDirection(theGraph, J, EDGEFLAG_DIRECTION_INONLY))
467
return NOTOK;
468
469
if (theGraph->G[J].v > I)
470
Row[theGraph->G[J].v] = '1';
471
472
J = gp_GetNextArc(theGraph, J);
473
}
474
475
Row[theGraph->N] = '\0';
476
fprintf(Outfile, "%s\n", Row);
477
}
478
479
free(Row);
480
return OK;
481
}
482
483
/********************************************************************
484
_WriteDebugInfo()
485
Writes adjacency list, but also includes the type value of each
486
edge (e.g. is it DFS child arc, forward arc or back arc?), and
487
the L, A and DFSParent of each vertex.
488
********************************************************************/
489
490
int _WriteDebugInfo(graphP theGraph, FILE *Outfile)
491
{
492
int I, J, Gsize;
493
494
if (theGraph==NULL || Outfile==NULL) return NOTOK;
495
496
/* Print parent copy vertices and their adjacency lists */
497
498
fprintf(Outfile, "DEBUG N=%d M=%d\n", theGraph->N, theGraph->M);
499
for (I=0; I < theGraph->N; I++)
500
{
501
fprintf(Outfile, "%d(P=%d,lA=%d,LowPt=%d,v=%d):",
502
I, theGraph->V[I].DFSParent,
503
theGraph->V[I].leastAncestor,
504
theGraph->V[I].Lowpoint,
505
theGraph->G[I].v);
506
507
J = gp_GetFirstArc(theGraph, I);
508
while (gp_IsArc(theGraph, J))
509
{
510
fprintf(Outfile, " %d(J=%d)", theGraph->G[J].v, J);
511
J = gp_GetNextArc(theGraph, J);
512
}
513
514
fprintf(Outfile, " %d\n", NIL);
515
}
516
517
/* Print any root copy vertices and their adjacency lists */
518
519
for (I = theGraph->N; I < 2*theGraph->N; I++)
520
{
521
if (theGraph->G[I].v == NIL)
522
continue;
523
524
fprintf(Outfile, "%d(copy of=%d, DFS child=%d):",
525
I, theGraph->G[I].v, I-theGraph->N);
526
527
J = gp_GetFirstArc(theGraph, I);
528
while (gp_IsArc(theGraph, J))
529
{
530
fprintf(Outfile, " %d(J=%d)", theGraph->G[J].v, J);
531
J = gp_GetNextArc(theGraph, J);
532
}
533
534
fprintf(Outfile, " %d\n", NIL);
535
}
536
537
/* Print information about vertices and root copy (virtual) vertices */
538
fprintf(Outfile, "\nVERTEX INFORMATION\n");
539
for (I=0; I < 2*theGraph->N; I++)
540
{
541
if (theGraph->G[I].v == NIL)
542
continue;
543
544
fprintf(Outfile, "V[%3d] v=%3d, type=%c, first arc=%3d, last arc=%3d\n",
545
I,
546
theGraph->G[I].v,
547
theGraph->G[I].type,
548
gp_GetFirstArc(theGraph, I),
549
gp_GetLastArc(theGraph, I));
550
}
551
552
/* Print information about edges */
553
554
fprintf(Outfile, "\nEDGE INFORMATION\n");
555
Gsize = theGraph->edgeOffset + theGraph->arcCapacity;
556
for (J=theGraph->edgeOffset; J < Gsize; J++)
557
{
558
if (theGraph->G[J].v == NIL)
559
continue;
560
561
fprintf(Outfile, "E[%3d] v=%3d, type=%c, next arc=%3d, prev arc=%3d\n",
562
J,
563
theGraph->G[J].v,
564
theGraph->G[J].type,
565
gp_GetNextArc(theGraph, J),
566
gp_GetPrevArc(theGraph, J));
567
}
568
569
return OK;
570
}
571
572
/********************************************************************
573
gp_Write()
574
Writes theGraph into the file.
575
Pass "stdout" or "stderr" to FileName to write to the corresponding stream
576
Pass WRITE_ADJLIST, WRITE_ADJMATRIX or WRITE_DEBUGINFO for the Mode
577
578
NOTE: For digraphs, it is an error to use a mode other than WRITE_ADJLIST
579
580
Returns NOTOK on error, OK on success.
581
********************************************************************/
582
583
int gp_Write(graphP theGraph, char *FileName, int Mode)
584
{
585
FILE *Outfile;
586
int RetVal;
587
588
if (theGraph == NULL || FileName == NULL)
589
return NOTOK;
590
591
if (strcmp(FileName, "nullwrite") == 0)
592
return OK;
593
594
if (strcmp(FileName, "stdout") == 0)
595
Outfile = stdout;
596
else if (strcmp(FileName, "stderr") == 0)
597
Outfile = stderr;
598
else if ((Outfile = fopen(FileName, WRITETEXT)) == NULL)
599
return NOTOK;
600
601
switch (Mode)
602
{
603
case WRITE_ADJLIST :
604
RetVal = _WriteAdjList(theGraph, Outfile);
605
break;
606
case WRITE_ADJMATRIX :
607
RetVal = _WriteAdjMatrix(theGraph, Outfile);
608
break;
609
case WRITE_DEBUGINFO :
610
RetVal = _WriteDebugInfo(theGraph, Outfile);
611
break;
612
default :
613
RetVal = NOTOK;
614
break;
615
}
616
617
if (RetVal == OK)
618
{
619
void *extraData = NULL;
620
long extraDataSize;
621
622
RetVal = theGraph->functions.fpWritePostprocess(theGraph, &extraData, &extraDataSize);
623
624
if (extraData != NULL)
625
{
626
if (!fwrite(extraData, extraDataSize, 1, Outfile))
627
RetVal = NOTOK;
628
free(extraData);
629
}
630
}
631
632
if (strcmp(FileName, "stdout") == 0 || strcmp(FileName, "stderr") == 0)
633
fflush(Outfile);
634
635
else if (fclose(Outfile) != 0)
636
RetVal = NOTOK;
637
638
return RetVal;
639
}
640
641
/********************************************************************
642
_WritePostprocess()
643
644
By default, no additional information is written.
645
********************************************************************/
646
647
int _WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize)
648
{
649
return OK;
650
}
651
652
/********************************************************************
653
_Log()
654
655
When the project is compiled with LOGGING enabled, this method writes
656
a string to the file PLANARITY.LOG in the current working directory.
657
On first write, the file is created or cleared.
658
Call this method with NULL to close the log file.
659
********************************************************************/
660
661
void _Log(char *Str)
662
{
663
static FILE *logfile = NULL;
664
665
if (logfile == NULL)
666
{
667
if ((logfile = fopen("PLANARITY.LOG", WRITETEXT)) == NULL)
668
return;
669
}
670
671
if (Str != NULL)
672
{
673
fprintf(logfile, "%s", Str);
674
fflush(logfile);
675
}
676
else
677
fclose(logfile);
678
}
679
680
void _LogLine(char *Str)
681
{
682
_Log(Str);
683
_Log("\n");
684
}
685
686
static char LogStr[512];
687
688
char *_MakeLogStr1(char *format, int one)
689
{
690
sprintf(LogStr, format, one);
691
return LogStr;
692
}
693
694
char *_MakeLogStr2(char *format, int one, int two)
695
{
696
sprintf(LogStr, format, one, two);
697
return LogStr;
698
}
699
700
char *_MakeLogStr3(char *format, int one, int two, int three)
701
{
702
sprintf(LogStr, format, one, two, three);
703
return LogStr;
704
}
705
706
char *_MakeLogStr4(char *format, int one, int two, int three, int four)
707
{
708
sprintf(LogStr, format, one, two, three, four);
709
return LogStr;
710
}
711
712
char *_MakeLogStr5(char *format, int one, int two, int three, int four, int five)
713
{
714
sprintf(LogStr, format, one, two, three, four, five);
715
return LogStr;
716
}
717
718