I can't solve this.

This mapping is unfinished: 9580 of 9580 nodes are still unmarked.

Before

1/*
2Copyright (c) 1997-2025, John M. Boyer
3All rights reserved.
4See the LICENSE.TXT file for licensing information.
5*/
6
7#include "planarity.h"
8
9/****************************************************************************
10 Configuration
11 ****************************************************************************/
12
13char Mode = 'r',
14 OrigOut = 'n',
15 OrigOutFormat = 'a',
16 EmbeddableOut = 'n',
17 ObstructedOut = 'n',
18 AdjListsForEmbeddingsOut = 'n';
19
20void Reconfigure(void)
21{
22 fflush(stdin);
23
24 Prompt("\nDo you want to \n"
25 " Randomly generate graphs (r),\n"
26 " Specify a graph (s),\n"
27 " Randomly generate a maximal planar graph (m), or\n"
28 " Randomly generate a non-planar graph (n)? ");
29 scanf(" %c", &Mode);
30
31 Mode = (char)tolower(Mode);
32 if (!strchr("rsmn", Mode))
33 Mode = 's';
34
35 if (Mode == 'r')
36 {
37 Message("\nNOTE: The directories for the graphs you want must exist.\n\n");
38
39 Prompt("Do you want original graphs in directory 'random'? (y/n) ");
40 scanf(" %c", &OrigOut);
41
42 if (tolower(OrigOut) == 'y')
43 {
44 Prompt("Do you want to output generated graphs to Adjacency List (last 10 only) or to G6 (all)? (a/g) ");
45 scanf(" %c", &OrigOutFormat);
46 }
47
48 Prompt("Do you want adj. matrix of embeddable graphs in directory 'embedded' (last 10 max))? (y/n) ");
49 scanf(" %c", &EmbeddableOut);
50
51 Prompt("Do you want adj. matrix of obstructed graphs in directory 'obstructed' (last 10 max)? (y/n) ");
52 scanf(" %c", &ObstructedOut);
53
54 Prompt("Do you want adjacency list format of embeddings in directory 'adjlist' (last 10 max)? (y/n) ");
55 scanf(" %c", &AdjListsForEmbeddingsOut);
56 }
57
58 FlushConsole(stdout);
59}
60
61void FlushConsole(FILE *f)
62{
63 fflush(f);
64}
65
66void Prompt(char const *message)
67{
68 Message(message);
69 FlushConsole(stdout);
70}
71
72/****************************************************************************
73 ****************************************************************************/
74
75void SaveAsciiGraph(graphP theGraph, char *filename)
76{
77 int e, EsizeOccupied, vertexLabelFix;
78 FILE *outfile = fopen(filename, WRITETEXT);
79
80 // The filename may specify a directory that doesn't exist
81 if (outfile == NULL)
82 {
83 char messageContents[MAXLINE + 1];
84 char const *messageFormat = "Failed to write to \"%.*s\"\nMake the directory if not present\n";
85 int charsAvailForStrToInclude = (int)(MAXLINE - strlen(messageFormat));
86#pragma GCC diagnostic push
87#pragma GCC diagnostic ignored "-Wformat-nonliteral"
88 sprintf(messageContents, messageFormat, charsAvailForStrToInclude, filename);
89#pragma GCC diagnostic pop
90 ErrorMessage(messageContents);
91 return;
92 }
93
94 // If filename includes path elements, remove them before writing the file's name to the file
95 if (strrchr(filename, FILE_DELIMITER))
96 filename = strrchr(filename, FILE_DELIMITER) + 1;
97
98 fprintf(outfile, "%s\n", filename);
99
100 // This edge list file format uses 1-based vertex numbering, and the current code
101 // internally uses 1-based indexing by default, so this vertex label 'fix' adds zero
102 // But earlier code used 0-based indexing and added one on output, so we replicate
103 // that behavior in case the current code has been compiled with zero-based indexing.
104 vertexLabelFix = 1 - gp_GetFirstVertex(theGraph);
105
106 // Iterate over the edges of the graph
107 EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
108 for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e += 2)
109 {
110 // Only output edges that haven't been deleted (i.e. skip the edge holes)
111 if (gp_EdgeInUse(theGraph, e))
112 {
113 fprintf(outfile, "%d %d\n",
114 gp_GetNeighbor(theGraph, e) + vertexLabelFix,
115 gp_GetNeighbor(theGraph, e + 1) + vertexLabelFix);
116 }
117 }
118
119 // Since vertex numbers are at least 1, this indicates the end of the edge list
120 fprintf(outfile, "0 0\n");
121
122 fclose(outfile);
123}
124
125/****************************************************************************
126 ReadTextFileIntoString()
127 Reads the file content indicated by infileName using a single fread(), and
128 returns the result in an allocated string. The caller needs to free() the
129 returned string when done with it.
130 Returns NULL on error, or an allocated string containing the file content.
131 ****************************************************************************/
132
133char *ReadTextFileIntoString(char const *infileName)
134{
135 FILE *infile = NULL;
136 char *inputString = NULL;
137
138 if ((infile = fopen(infileName, "r")) == NULL)
139 ErrorMessage("fopen() failed.\n");
140 else
141 {
142 long filePos = ftell(infile);
143 long fileSize;
144
145 fseek(infile, 0, SEEK_END);
146 fileSize = ftell(infile);
147 fseek(infile, filePos, SEEK_SET);
148
149 if ((inputString = (char *)malloc((fileSize + 1) * sizeof(char))) != NULL)
150 {
151 long bytesRead = fread((void *)inputString, 1, fileSize, infile);
152 inputString[bytesRead] = '\0';
153 }
154
155 fclose(infile);
156 }
157
158 return inputString;
159}
160
161/****************************************************************************
162 * TextFileMatchesString()
163 *
164 * Compares the text file content from the file named 'theFilename' with
165 * the content of 'theString'.
166 *
167 * Textual equality is measured as content equality except for suppressing
168 * differences between CRLF and LF-only line delimiters.
169 *
170 * Returns TRUE if the contents are textually equal, FALSE otherwise
171 ****************************************************************************/
172
173int TextFileMatchesString(char const *theFilename, char const *theString)
174{
175 FILE *infile = NULL;
176 int Result = TRUE;
177
178 if (theFilename != NULL)
179 infile = fopen(theFilename, "r");
180
181 if (infile == NULL || theString == NULL)
182 Result = FALSE;
183 else
184 {
185 int c1 = 0, c2 = 0, stringIndex = 0;
186
187 // Read the input file to the end
188 while ((c1 = fgetc(infile)) != EOF)
189 {
190 // Want to suppress distinction between lines ending with CRLF versus LF
191 // by looking at only LF characters in the file
192 if (c1 == '\r')
193 continue;
194
195 // Since c1 now has a non-CR, non-EOF from the input file, we now also
196 // get a character from the string, except ignoring CRs again
197 while ((c2 = (int)theString[stringIndex++]) == '\r')
198 ;
199
200 // If c1 doesn't equal c2 (whether c2 is a null terminator or a different character)
201 // then the file content doesn't match the string
202 if (c1 != c2)
203 {
204 Result = FALSE;
205 break;
206 }
207 }
208
209 // If the outer while loop got to the end of the file
210 if (c1 == EOF)
211 {
212 // Then get another character from the string, once again suppressing CRs, and then...
213 while ((c2 = (int)theString[stringIndex++]) == '\r')
214 ;
215 // Test whether or not the second file also ends, same as the first.
216 if (c2 != '\0')
217 Result = FALSE;
218 }
219 }
220
221 if (infile != NULL)
222 fclose(infile);
223 return Result;
224}
225
226/****************************************************************************
227 ****************************************************************************/
228
229int TextFilesEqual(char *file1Name, char *file2Name)
230{
231 FILE *infile1 = NULL, *infile2 = NULL;
232 int Result = TRUE;
233
234 infile1 = fopen(file1Name, "r");
235 infile2 = fopen(file2Name, "r");
236
237 if (infile1 == NULL || infile2 == NULL)
238 Result = FALSE;
239 else
240 {
241 int c1 = 0, c2 = 0;
242
243 // Read the first file to the end
244 while ((c1 = fgetc(infile1)) != EOF)
245 {
246 // Want to suppress distinction between lines ending with CRLF versus LF
247 if (c1 == '\r')
248 continue;
249
250 // Get a char from the second file, except suppress CR again
251 while ((c2 = fgetc(infile2)) == '\r')
252 ;
253
254 // If we got a char from the first file, but not from the second
255 // then the second file is shorter, so files are not equal
256 if (c2 == EOF)
257 {
258 Result = FALSE;
259 break;
260 }
261
262 // If we got a char from second file, but not equal to char from
263 // first file, then files are not equal
264 if (c1 != c2)
265 {
266 Result = FALSE;
267 break;
268 }
269 }
270
271 // If we got to the end of the first file without breaking the loop...
272 if (c1 == EOF)
273 {
274 // Then, once again, suppress CRs first, and then...
275 while ((c2 = fgetc(infile2)) == '\r')
276 ;
277 // Test whether or not the second file also ends, same as the first.
278 if (fgetc(infile2) != EOF)
279 Result = FALSE;
280 }
281 }
282
283 if (infile1 != NULL)
284 fclose(infile1);
285 if (infile2 != NULL)
286 fclose(infile2);
287 return Result;
288}
289
290/****************************************************************************
291 ****************************************************************************/
292
293int BinaryFilesEqual(char *file1Name, char *file2Name)
294{
295 FILE *infile1 = NULL, *infile2 = NULL;
296 int Result = TRUE;
297
298 infile1 = fopen(file1Name, "r");
299 infile2 = fopen(file2Name, "r");
300
301 if (infile1 == NULL || infile2 == NULL)
302 Result = FALSE;
303 else
304 {
305 int c1 = 0, c2 = 0;
306
307 // Read the first file to the end
308 while ((c1 = fgetc(infile1)) != EOF)
309 {
310 // If we got a char from the first file, but not from the second
311 // then the second file is shorter, so files are not equal
312 if ((c2 = fgetc(infile2)) == EOF)
313 {
314 Result = FALSE;
315 break;
316 }
317
318 // If we got a char from second file, but not equal to char from
319 // first file, then files are not equal
320 if (c1 != c2)
321 {
322 Result = FALSE;
323 break;
324 }
325 }
326
327 // If we got to the end of the first file without breaking the loop...
328 if (c1 == EOF)
329 {
330 // Then attempt to read from the second file to ensure it also ends.
331 if (fgetc(infile2) != EOF)
332 Result = FALSE;
333 }
334 }
335
336 if (infile1 != NULL)
337 fclose(infile1);
338 if (infile2 != NULL)
339 fclose(infile2);
340 return Result;
341}
342
343/****************************************************************************
344 ALGORITHM FLAGS/SPECIFIERS
345****************************************************************************/
346
347char const *GetAlgorithmFlags(void)
348{
349 return "C = command (algorithm implementation to run)\n"
350 " -p = Planar embedding and Kuratowski subgraph isolation\n"
351 " -d = Planar graph drawing by visibility representation\n"
352 " -o = Outerplanar embedding and obstruction isolation\n"
353 " -2 = Search for subgraph homeomorphic to K_{2,3}\n"
354 " -3 = Search for subgraph homeomorphic to K_{3,3}\n"
355 " -4 = Search for subgraph homeomorphic to K_4\n"
356 "\n";
357}
358
359char const *GetAlgorithmSpecifiers(void)
360{
361 return "P. Planar embedding and Kuratowski subgraph isolation\n"
362 "D. Planar graph drawing by visibility representation\n"
363 "O. Outerplanar embedding and obstruction isolation\n"
364 "2. Search for subgraph homeomorphic to K_{2,3}\n"
365 "3. Search for subgraph homeomorphic to K_{3,3}\n"
366 "4. Search for subgraph homeomorphic to K_4\n";
367}
368
369char const *GetAlgorithmChoices(void)
370{
371 return "pdo234";
372}
373
374/****************************************************************************
375 ****************************************************************************/
376
377int GetEmbedFlags(char command)
378{
379 int embedFlags = 0;
380
381 switch (command)
382 {
383 case 'o':
384 embedFlags = EMBEDFLAGS_OUTERPLANAR;
385 break;
386 case 'p':
387 embedFlags = EMBEDFLAGS_PLANAR;
388 break;
389 case 'd':
390 embedFlags = EMBEDFLAGS_DRAWPLANAR;
391 break;
392 case '2':
393 embedFlags = EMBEDFLAGS_SEARCHFORK23;
394 break;
395 case '3':
396 embedFlags = EMBEDFLAGS_SEARCHFORK33;
397 break;
398 case '4':
399 embedFlags = EMBEDFLAGS_SEARCHFORK4;
400 break;
401 default:
402 break;
403 }
404
405 return embedFlags;
406}
407
408/****************************************************************************
409 ****************************************************************************/
410
411char const *GetAlgorithmName(char command)
412{
413 char const *algorithmName = "UnsupportedAlgorithm";
414
415 switch (command)
416 {
417 case 'p':
418 algorithmName = "PlanarEmbed";
419 break;
420 case 'd':
421 algorithmName = DRAWPLANAR_NAME;
422 break;
423 case 'o':
424 algorithmName = "OuterplanarEmbed";
425 break;
426 case '2':
427 algorithmName = K23SEARCH_NAME;
428 break;
429 case '3':
430 algorithmName = K33SEARCH_NAME;
431 break;
432 case '4':
433 algorithmName = K4SEARCH_NAME;
434 break;
435 default:
436 break;
437 }
438
439 return algorithmName;
440}
441
442/****************************************************************************
443 ****************************************************************************/
444
445char const *GetTransformationName(char command)
446{
447 char const *transformationName = "UnsupportedTransformation";
448
449 switch (command)
450 {
451 case 'g':
452 transformationName = "G6";
453 break;
454 case 'a':
455 transformationName = "AdjList";
456 break;
457 case 'm':
458 transformationName = "AdjMat";
459 break;
460 default:
461 break;
462 }
463
464 return transformationName;
465}
466
467char const *GetSupportedOutputChoices(void)
468{
469 return "G. G6 format\n"
470 "A. Adjacency List format\n"
471 "M. Adjacency Matrix format\n";
472}
473
474char const *GetSupportedOutputFormats(void)
475{
476 return "gam";
477}
478
479/****************************************************************************
480 ****************************************************************************/
481
482char const *GetBaseName(int baseFlag)
483{
484 char const *transformationName = baseFlag ? "1-based" : "0-based";
485
486 return transformationName;
487}
488
489/****************************************************************************
490 ****************************************************************************/
491
492void AttachAlgorithm(graphP theGraph, char command)
493{
494 switch (command)
495 {
496 case 'd':
497 gp_AttachDrawPlanar(theGraph);
498 break;
499 case '2':
500 gp_AttachK23Search(theGraph);
501 break;
502 case '3':
503 gp_AttachK33Search(theGraph);
504 break;
505 case '4':
506 gp_AttachK4Search(theGraph);
507 break;
508 default:
509 break;
510 }
511}
512
513/****************************************************************************
514 A string used to construct input and output filenames.
515
516 The SUFFIXMAXLENGTH is 32 to accommodate ".out.txt" + ".render.txt" + ".test.txt"
517 ****************************************************************************/
518
519#define FILENAMEMAXLENGTH 128
520#define ALGORITHMNAMEMAXLENGTH 32
521#define SUFFIXMAXLENGTH 32
522
523char theFileName[FILENAMEMAXLENGTH + 1 + ALGORITHMNAMEMAXLENGTH + 1 + SUFFIXMAXLENGTH + 1];
524
525/****************************************************************************
526 ConstructInputFilename()
527 Returns a string not owned by the caller (do not free string).
528 String contains infileName content if infileName is non-NULL.
529 If infileName is NULL, then the user is asked to supply a name.
530 Returns NULL on error, or a non-NULL string on success.
531 ****************************************************************************/
532
533char *ConstructInputFilename(char const *infileName)
534{
535 if (infileName == NULL)
536 {
537 do
538 {
539 Prompt("Enter graph file name: ");
540 fflush(stdin);
541 scanf(" %s", theFileName);
542
543 if (strncmp(theFileName, "stdin", strlen("stdin")) != 0 && !strchr(theFileName, '.'))
544 {
545 Message("Graph file name does not have extension; automatically appending \".txt\".\n");
546 strcat(theFileName, ".txt");
547 }
548 } while (strlen(theFileName) == 0);
549 }
550 else
551 {
552 if (strlen(infileName) > FILENAMEMAXLENGTH)
553 {
554 ErrorMessage("Filename is too long");
555 return NULL;
556 }
557 else if (strlen(infileName) == 0)
558 {
559 ErrorMessage("Filename is empty");
560 return NULL;
561 }
562 strcpy(theFileName, infileName);
563 }
564
565 return theFileName;
566}
567
568/****************************************************************************
569 ConstructPrimaryOutputFilename()
570 Returns a string not owned by the caller (do not free string).
571 Reuses the same memory space as ConstructInputFilename().
572 If outfileName is non-NULL, then the result string contains its content.
573 If outfileName is NULL, then the infileName and the command's algorithm name
574 are used to construct a string.
575 Returns non-NULL string
576 ****************************************************************************/
577
578char *ConstructPrimaryOutputFilename(char const *infileName, char const *outfileName, char command)
579{
580 char const *algorithmName = GetAlgorithmName(command);
581
582 if (outfileName == NULL)
583 {
584 // The output filename is based on the input filename
585 if (theFileName != infileName)
586 strcpy(theFileName, infileName);
587
588 // If the primary output filename has not been given, then we use
589 // the input filename + the algorithm name + a simple suffix
590 if (strlen(algorithmName) <= ALGORITHMNAMEMAXLENGTH)
591 {
592 strcat(theFileName, ".");
593 strcat(theFileName, algorithmName);
594 }
595 else
596 ErrorMessage("Algorithm Name is too long, so it will not be used in output filename.");
597
598 strcat(theFileName, ".out.txt");
599 }
600 else
601 {
602 if (strlen(outfileName) > FILENAMEMAXLENGTH)
603 {
604 char const *messageFormat = "Outfile filename is too long. Result placed in \"%.*s\"";
605 int charsAvailForStrToInclude = (int)(MAXLINE - strlen(messageFormat));
606 char messageContents[MAXLINE + 1];
607 messageContents[0] = '\0';
608
609 // The output filename is based on the input filename
610 if (theFileName != infileName)
611 strcpy(theFileName, infileName);
612
613 if (strlen(algorithmName) <= ALGORITHMNAMEMAXLENGTH)
614 {
615 strcat(theFileName, ".");
616 strcat(theFileName, algorithmName);
617 }
618 strcat(theFileName, ".out.txt");
619
620#pragma GCC diagnostic push
621#pragma GCC diagnostic ignored "-Wformat-nonliteral"
622 sprintf(messageContents, messageFormat, charsAvailForStrToInclude, theFileName);
623#pragma GCC diagnostic pop
624 ErrorMessage(messageContents);
625 }
626 else
627 {
628 if (theFileName != outfileName)
629 strcpy(theFileName, outfileName);
630 }
631 }
632
633 return theFileName;
634}
635
636/****************************************************************************
637 ConstructTransformationExpectedResultFilename()
638 Returns a string whose ownership will be transferred to the caller (must free string).
639 If outfileName is non-NULL, then the result string contains its content.
640 If outfileName is NULL, then the infileName, the command's algorithm name, and
641 whether or not this output file correspond to the actual (0) or expected (1)
642 output file from testing are used to construct a string.
643 Returns non-NULL string
644 ****************************************************************************/
645
646int ConstructTransformationExpectedResultFilename(char const *infileName, char **outfileName, char command, int baseFlag)
647{
648 int Result = OK;
649 char const *baseName = GetBaseName(baseFlag);
650 char const *transformationName = GetTransformationName(command);
651 int infileNameLen = -1;
652
653 if (infileName == NULL || (infileNameLen = strlen(infileName)) < 1)
654 {
655 ErrorMessage("Cannot construct transformation output filename for empty infileName.\n");
656 return NOTOK;
657 }
658
659 if ((*outfileName) == NULL)
660 {
661 (*outfileName) = (char *)calloc(infileNameLen + 1 + strlen(baseName) + 1 + strlen(transformationName) + ((command == 'g') ? strlen(".out.g6") : strlen(".out.txt")) + 1, sizeof(char));
662
663 if ((*outfileName) == NULL)
664 {
665 ErrorMessage("Unable to allocate memory for output filename.\n");
666 return NOTOK;
667 }
668
669 strcpy((*outfileName), infileName);
670 strcat((*outfileName), ".");
671 strcat((*outfileName), baseName);
672 strcat((*outfileName), ".");
673 strcat((*outfileName), transformationName);
674 strcat((*outfileName), command == 'g' ? ".out.g6" : ".out.txt");
675 }
676 else
677 {
678 ErrorMessage("outfileName already allocated.\n");
679 Result = NOTOK;
680 }
681
682 return Result;
683}
684

After

1/*
2Copyright (c) 1997-2025, John M. Boyer
3All rights reserved.
4See the LICENSE.TXT file for licensing information.
5*/
6
7#include "planarity.h"
8
9/****************************************************************************
10 Configuration
11 ****************************************************************************/
12
13char Mode = 'r',
14 OrigOut = 'n',
15 OrigOutFormat = 'a',
16 EmbeddableOut = 'n',
17 ObstructedOut = 'n',
18 AdjListsForEmbeddingsOut = 'n';
19
20int Reconfigure(void)
21{
22 int Result = OK;
23
24 char lineBuff[MAXLINE + 1];
25
26 memset(lineBuff, '\0', (MAXLINE + 1));
27
28 while (1)
29 {
30 Prompt("\nDo you want to \n"
31 " Randomly generate graphs (r),\n"
32 " Specify a graph (s),\n"
33 " Randomly generate a maximal planar graph (m), or\n"
34 " Randomly generate a non-planar graph (n)?\n\t");
35
36 if (GetLineFromStdin(lineBuff, MAXLINE) != OK)
37 {
38 ErrorMessage("Unable to fetch reconfigure choice from stdin.\n");
39 Result = NOTOK;
40 break;
41 }
42
43 if (strlen(lineBuff) != 1 ||
44 sscanf(lineBuff, " %c", &Mode) != 1 ||
45 !strchr("rsmn", tolower(Mode)))
46 ErrorMessage("Invalid choice for Mode.\n");
47 else
48 {
49 Mode = (char)tolower(Mode);
50 break;
51 }
52 }
53
54 if (Result == OK && Mode == 'r')
55 {
56 Message("\nNOTE: The directories for the graphs you want must exist.\n\n");
57
58 while (1)
59 {
60 Prompt("Do you want original graphs in directory 'random'? (y/n) ");
61 if (GetLineFromStdin(lineBuff, MAXLINE) != OK)
62 {
63 ErrorMessage("Unable to fetch choice from stdin.\n");
64 Result = NOTOK;
65 break;
66 }
67
68 if (strlen(lineBuff) != 1 ||
69 sscanf(lineBuff, " %c", &OrigOut) != 1 ||
70 !strchr(YESNOCHOICECHARS, OrigOut))
71 ErrorMessage("Invalid choice.\n");
72 else
73 {
74 OrigOut = (char)tolower(OrigOut);
75 break;
76 }
77 }
78
79 if (Result == OK && OrigOut == 'y')
80 {
81 while (1)
82 {
83 Prompt("Do you want to output generated graphs to Adjacency List (last 10 only) or to G6 (all)? (a/g) ");
84 if (GetLineFromStdin(lineBuff, MAXLINE) != OK)
85 {
86 ErrorMessage("Unable to fetch choice from stdin.\n");
87 Result = NOTOK;
88 break;
89 }
90
91 if (strlen(lineBuff) != 1 ||
92 sscanf(lineBuff, " %c", &OrigOutFormat) != 1 ||
93 !strchr("aAgG", OrigOutFormat))
94 ErrorMessage("Invalid choice.\n");
95 else
96 {
97 OrigOutFormat = (char)tolower(OrigOutFormat);
98 break;
99 }
100 }
101 }
102
103 if (Result == OK)
104 {
105 while (1)
106 {
107 Prompt("Do you want adj. matrix of embeddable graphs in directory 'embedded' (last 10 max))? (y/n) ");
108 if (GetLineFromStdin(lineBuff, MAXLINE) != OK)
109 {
110 ErrorMessage("Unable to fetch choice from stdin.\n");
111 Result = NOTOK;
112 break;
113 }
114 if (strlen(lineBuff) != 1 ||
115 sscanf(lineBuff, " %c", &EmbeddableOut) != 1 ||
116 !strchr(YESNOCHOICECHARS, EmbeddableOut))
117 ErrorMessage("Invalid choice.\n");
118 else
119 {
120 EmbeddableOut = (char)tolower(EmbeddableOut);
121 break;
122 }
123 }
124 }
125
126 if (Result == OK)
127 {
128 while (1)
129 {
130 Prompt("Do you want adj. matrix of obstructed graphs in directory 'obstructed' (last 10 max)? (y/n) ");
131 if (GetLineFromStdin(lineBuff, MAXLINE) != OK)
132 {
133 ErrorMessage("Unable to fetch choice from stdin.\n");
134 Result = NOTOK;
135 break;
136 }
137
138 if (strlen(lineBuff) != 1 ||
139 sscanf(lineBuff, " %c", &ObstructedOut) != 1 ||
140 !strchr(YESNOCHOICECHARS, ObstructedOut))
141 ErrorMessage("Invalid choice.\n");
142 else
143 {
144 ObstructedOut = (char)tolower(ObstructedOut);
145 break;
146 }
147 }
148 }
149
150 if (Result == OK)
151 {
152 while (1)
153 {
154 Prompt("Do you want adjacency list format of embeddings in directory 'adjlist' (last 10 max)? (y/n) ");
155 if (GetLineFromStdin(lineBuff, MAXLINE) != OK)
156 {
157 ErrorMessage("Unable to fetch choice from stdin.\n");
158 Result = NOTOK;
159 break;
160 }
161
162 if (strlen(lineBuff) != 1 ||
163 sscanf(lineBuff, " %c", &AdjListsForEmbeddingsOut) != 1 ||
164 !strchr(YESNOCHOICECHARS, AdjListsForEmbeddingsOut))
165 ErrorMessage("Invalid choice.\n");
166 else
167 {
168 AdjListsForEmbeddingsOut = (char)tolower(AdjListsForEmbeddingsOut);
169 break;
170 }
171 }
172 }
173 }
174
175 FlushConsole(stdout);
176
177 return Result;
178}
179
180int GetLineFromStdin(char *lineBuff, int lineBuffSize)
181{
182 if (lineBuff == NULL)
183 {
184 ErrorMessage("Line buffer to populate is NULL.\n");
185 return NOTOK;
186 }
187
188 memset(lineBuff, '\0', lineBuffSize);
189
190 if (fgets(lineBuff, lineBuffSize, stdin) == NULL && ferror(stdin))
191 {
192 ErrorMessage("Call to fgets() from stdin failed.\n");
193 return NOTOK;
194 }
195
196 // From https://stackoverflow.com/a/28462221, strcspn finds the index of the
197 // first char in charset; this way, I replace the char at that index with
198 // the null-terminator
199 // N.B. See https://cplusplus.com/reference/cstring/strcspn/ : if no chars
200 // in str2 appear in str1, will return strlen(str1), so this line is a no-op
201 lineBuff[strcspn(lineBuff, "\n\r")] = '\0'; // works for LF, CR, CRLF, LFCR, ...
202
203 return OK;
204}
205
206void FlushConsole(FILE *f)
207{
208 // N.B. fflush(stdin) constitutes undefined behaviour; see:
209 // https://c-faq.com/stdio/stdinflush.html
210 if (f == stdin)
211 return;
212 fflush(f);
213}
214
215void Prompt(char const *message)
216{
217 Message(message);
218 FlushConsole(stdout);
219}
220
221/****************************************************************************
222 ****************************************************************************/
223
224void SaveAsciiGraph(graphP theGraph, char *filename)
225{
226 int e, EsizeOccupied, vertexLabelFix;
227 FILE *outfile = fopen(filename, WRITETEXT);
228
229 // The filename may specify a directory that doesn't exist
230 if (outfile == NULL)
231 {
232 char messageContents[MAXLINE + 1];
233 char const *messageFormat = "Failed to write to \"%.*s\"\nMake the directory if not present\n";
234 int charsAvailForStrToInclude = (int)(MAXLINE - strlen(messageFormat));
235#pragma GCC diagnostic push
236#pragma GCC diagnostic ignored "-Wformat-nonliteral"
237 sprintf(messageContents, messageFormat, charsAvailForStrToInclude, filename);
238#pragma GCC diagnostic pop
239 ErrorMessage(messageContents);
240 return;
241 }
242
243 // If filename includes path elements, remove them before writing the file's name to the file
244 if (strrchr(filename, FILE_DELIMITER))
245 filename = strrchr(filename, FILE_DELIMITER) + 1;
246
247 fprintf(outfile, "%s\n", filename);
248
249 // This edge list file format uses 1-based vertex numbering, and the current code
250 // internally uses 1-based indexing by default, so this vertex label 'fix' adds zero
251 // But earlier code used 0-based indexing and added one on output, so we replicate
252 // that behavior in case the current code has been compiled with zero-based indexing.
253 vertexLabelFix = 1 - gp_GetFirstVertex(theGraph);
254
255 // Iterate over the edges of the graph
256 EsizeOccupied = gp_EdgeInUseIndexBound(theGraph);
257 for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e += 2)
258 {
259 // Only output edges that haven't been deleted (i.e. skip the edge holes)
260 if (gp_EdgeInUse(theGraph, e))
261 {
262 fprintf(outfile, "%d %d\n",
263 gp_GetNeighbor(theGraph, e) + vertexLabelFix,
264 gp_GetNeighbor(theGraph, e + 1) + vertexLabelFix);
265 }
266 }
267
268 // Since vertex numbers are at least 1, this indicates the end of the edge list
269 fprintf(outfile, "0 0\n");
270
271 fclose(outfile);
272}
273
274/****************************************************************************
275 ReadTextFileIntoString()
276 Reads the file content indicated by infileName using a single fread(), and
277 returns the result in an allocated string. The caller needs to free() the
278 returned string when done with it.
279 Returns NULL on error, or an allocated string containing the file content.
280 ****************************************************************************/
281
282char *ReadTextFileIntoString(char const *infileName)
283{
284 FILE *infile = NULL;
285 char *inputString = NULL;
286
287 if ((infile = fopen(infileName, "r")) == NULL)
288 ErrorMessage("fopen() failed.\n");
289 else
290 {
291 long filePos = ftell(infile);
292 long fileSize;
293
294 fseek(infile, 0, SEEK_END);
295 fileSize = ftell(infile);
296 fseek(infile, filePos, SEEK_SET);
297
298 if ((inputString = (char *)malloc((fileSize + 1) * sizeof(char))) != NULL)
299 {
300 long bytesRead = fread((void *)inputString, 1, fileSize, infile);
301 inputString[bytesRead] = '\0';
302 }
303
304 fclose(infile);
305 }
306
307 return inputString;
308}
309
310/****************************************************************************
311 * TextFileMatchesString()
312 *
313 * Compares the text file content from the file named 'theFilename' with
314 * the content of 'theString'.
315 *
316 * Textual equality is measured as content equality except for suppressing
317 * differences between CRLF and LF-only line delimiters.
318 *
319 * Returns TRUE if the contents are textually equal, FALSE otherwise
320 ****************************************************************************/
321
322int TextFileMatchesString(char const *theFilename, char const *theString)
323{
324 int Result = TRUE;
325
326 FILE *infile = NULL;
327
328 if (theFilename != NULL)
329 infile = fopen(theFilename, "r");
330
331 if (infile == NULL || theString == NULL)
332 Result = FALSE;
333 else
334 {
335 int c1 = 0, c2 = 0, stringIndex = 0;
336
337 // Read the input file to the end
338 while ((c1 = fgetc(infile)) != EOF)
339 {
340 // Want to suppress distinction between lines ending with CRLF versus LF
341 // by looking at only LF characters in the file
342 if (c1 == '\r')
343 continue;
344
345 // Since c1 now has a non-CR, non-EOF from the input file, we now also
346 // get a character from the string, except ignoring CRs again
347 while ((c2 = (int)theString[stringIndex++]) == '\r')
348 ;
349
350 // If c1 doesn't equal c2 (whether c2 is a null terminator or a different character)
351 // then the file content doesn't match the string
352 if (c1 != c2)
353 {
354 Result = FALSE;
355 break;
356 }
357 }
358
359 // If the outer while loop got to the end of the file
360 if (c1 == EOF)
361 {
362 // Then get another character from the string, once again suppressing CRs, and then...
363 while ((c2 = (int)theString[stringIndex++]) == '\r')
364 ;
365 // Test whether or not the second file also ends, same as the first.
366 if (c2 != '\0')
367 Result = FALSE;
368 }
369 }
370
371 if (infile != NULL)
372 fclose(infile);
373
374 return Result;
375}
376
377/****************************************************************************
378 ****************************************************************************/
379
380int TextFilesEqual(char *file1Name, char *file2Name)
381{
382 int Result = TRUE;
383
384 FILE *infile1 = NULL, *infile2 = NULL;
385
386 infile1 = fopen(file1Name, "r");
387 infile2 = fopen(file2Name, "r");
388
389 if (infile1 == NULL || infile2 == NULL)
390 Result = FALSE;
391 else
392 {
393 int c1 = 0, c2 = 0;
394
395 // Read the first file to the end
396 while ((c1 = fgetc(infile1)) != EOF)
397 {
398 // Want to suppress distinction between lines ending with CRLF versus LF
399 if (c1 == '\r')
400 continue;
401
402 // Get a char from the second file, except suppress CR again
403 while ((c2 = fgetc(infile2)) == '\r')
404 ;
405
406 // If we got a char from the first file, but not from the second
407 // then the second file is shorter, so files are not equal
408 if (c2 == EOF)
409 {
410 Result = FALSE;
411 break;
412 }
413
414 // If we got a char from second file, but not equal to char from
415 // first file, then files are not equal
416 if (c1 != c2)
417 {
418 Result = FALSE;
419 break;
420 }
421 }
422
423 // If we got to the end of the first file without breaking the loop...
424 if (c1 == EOF)
425 {
426 // Then, once again, suppress CRs first, and then...
427 while ((c2 = fgetc(infile2)) == '\r')
428 ;
429 // Test whether or not the second file also ends, same as the first.
430 if (fgetc(infile2) != EOF)
431 Result = FALSE;
432 }
433 }
434
435 if (infile1 != NULL)
436 fclose(infile1);
437
438 if (infile2 != NULL)
439 fclose(infile2);
440
441 return Result;
442}
443
444/****************************************************************************
445 ****************************************************************************/
446
447int BinaryFilesEqual(char *file1Name, char *file2Name)
448{
449 int Result = TRUE;
450
451 FILE *infile1 = NULL, *infile2 = NULL;
452
453 infile1 = fopen(file1Name, "r");
454 infile2 = fopen(file2Name, "r");
455
456 if (infile1 == NULL || infile2 == NULL)
457 Result = FALSE;
458 else
459 {
460 int c1 = 0, c2 = 0;
461
462 // Read the first file to the end
463 while ((c1 = fgetc(infile1)) != EOF)
464 {
465 // If we got a char from the first file, but not from the second
466 // then the second file is shorter, so files are not equal
467 if ((c2 = fgetc(infile2)) == EOF)
468 {
469 Result = FALSE;
470 break;
471 }
472
473 // If we got a char from second file, but not equal to char from
474 // first file, then files are not equal
475 if (c1 != c2)
476 {
477 Result = FALSE;
478 break;
479 }
480 }
481
482 // If we got to the end of the first file without breaking the loop...
483 if (c1 == EOF)
484 {
485 // Then attempt to read from the second file to ensure it also ends.
486 if (fgetc(infile2) != EOF)
487 Result = FALSE;
488 }
489 }
490
491 if (infile1 != NULL)
492 fclose(infile1);
493
494 if (infile2 != NULL)
495 fclose(infile2);
496
497 return Result;
498}
499
500/****************************************************************************
501 ALGORITHM FLAGS/SPECIFIERS
502****************************************************************************/
503
504char const *GetAlgorithmFlags(void)
505{
506 return "C = command (algorithm implementation to run)\n"
507 " -p = Planar embedding and Kuratowski subgraph isolation\n"
508 " -d = Planar graph drawing by visibility representation\n"
509 " -o = Outerplanar embedding and obstruction isolation\n"
510 " -2 = Search for subgraph homeomorphic to K_{2,3}\n"
511 " -3 = Search for subgraph homeomorphic to K_{3,3}\n"
512 " -4 = Search for subgraph homeomorphic to K_4\n"
513 "\n";
514}
515
516char const *GetAlgorithmSpecifiers(void)
517{
518 return "P. Planar embedding and Kuratowski subgraph isolation\n"
519 "D. Planar graph drawing by visibility representation\n"
520 "O. Outerplanar embedding and obstruction isolation\n"
521 "2. Search for subgraph homeomorphic to K_{2,3}\n"
522 "3. Search for subgraph homeomorphic to K_{3,3}\n"
523 "4. Search for subgraph homeomorphic to K_4\n";
524}
525
526char const *GetAlgorithmChoices(void)
527{
528 return "pdo234";
529}
530
531/****************************************************************************
532 * GetCommandAndOptionalModifier()
533 *
534 * When provided with a valid commandString, determine the command character
535 * (and optional modifier) and return to the caller via the pointers to chars.
536 *
537 * Note that characters are processed with tolower(), since comparisons are
538 * typically performed only against lower-case letters; when applied to a digit
539 * (or to any other character where such conversion is not possible), tolower()
540 * returns the same character.
541 *
542 * Returns OK when command (and optional modifier) are successfully extracted
543 * from the commandString; no validation of the characters supplied is performed
544 * at this point.
545 *
546 * Returns NOTOK if the commandString is invalid, or if
547 * the pointer to the character to which you wish to assign the extracted
548 * character is NULL.
549 ****************************************************************************/
550
551int GetCommandAndOptionalModifier(const char *commandString, char *command, char *modifier)
552{
553 if (commandString != NULL && commandString[0] == '-')
554 commandString++;
555
556 if (commandString == NULL || strlen(commandString) == 0)
557 {
558 ErrorMessage("Cannot get embed flags for empty command string.\n");
559 return NOTOK;
560 }
561
562 if (command == NULL)
563 {
564 ErrorMessage("Pointer to character to which to write command is NULL.\n");
565 return NOTOK;
566 }
567
568 (*command) = '\0';
569
570 if (modifier != NULL)
571 (*modifier) = '\0';
572
573 if (strlen(commandString) == 1)
574 {
575 (*command) = (char)tolower(commandString[0]);
576 }
577 else if (strlen(commandString) == 2)
578 {
579 (*command) = (char)tolower(commandString[0]);
580 if (modifier != NULL)
581 (*modifier) = (char)tolower(commandString[1]);
582 }
583
584 return OK;
585}
586
587/****************************************************************************
588 * GetEmbedFlags()
589 *
590 * Derives the embedFlags when provided with a valid command character (and
591 * optionally a modifier)
592 *
593 * Returns OK if embedFlags successfully derived, NOTOK if command and/or
594 * modifier character is invalid, or if the pointer to the integer to which you
595 * wish to assign the derived embedFlags is NULL.
596 ****************************************************************************/
597
598int GetEmbedFlags(char command, char modifier, int *embedFlagsP)
599{
600 if (embedFlagsP == NULL)
601 {
602 ErrorMessage("Pointer to embedFlags int is NULL.\n");
603 return NOTOK;
604 }
605
606 switch (command)
607 {
608 case 'p':
609 (*embedFlagsP) = EMBEDFLAGS_PLANAR;
610 break;
611 case 'd':
612 (*embedFlagsP) = EMBEDFLAGS_DRAWPLANAR;
613 break;
614 case 'o':
615 (*embedFlagsP) = EMBEDFLAGS_OUTERPLANAR;
616 break;
617 case '2':
618 (*embedFlagsP) = EMBEDFLAGS_SEARCHFORK23;
619 break;
620 case '3':
621 (*embedFlagsP) = EMBEDFLAGS_SEARCHFORK33;
622 // Future TODO: Add logic: if command == '3' and modifier == 'e',
623 // then embedFlagsP |= EMBEDFLAGS_SEARCHEMBED (or similar)
624 // Future TODO: Error out if modifier char is anything other than 'e'
625 break;
626 case '4':
627 (*embedFlagsP) = EMBEDFLAGS_SEARCHFORK4;
628 break;
629 default:
630 ErrorMessage("Unrecognized algorithm command specifier.\n");
631 return NOTOK;
632 }
633
634 // Future TODO: Currently, there are no graph algorithm extensions for which
635 // we wish to specify a modifier; if an additional character after the
636 // command specifier is given, then error out. In the future, once there are
637 // graph algorithm extensions whose behaviour may be conditioned on a user-
638 // specified modifier character, this check should be removed.
639 if (modifier != '\0')
640 {
641 ErrorMessage("Algorithm modifiers currently not supported.\n");
642 return NOTOK;
643 }
644
645 return OK;
646}
647
648/****************************************************************************
649 * GetAlgorithmName()
650 *
651 * When provided with a valid command char, derives the corresponding algorithm
652 * name returned via char const * to the caller. This memory should not be
653 * freed by the caller.
654 * ****************************************************************************/
655
656char const *GetAlgorithmName(char command)
657{
658 char const *algorithmName = "UnsupportedAlgorithm";
659
660 switch (command)
661 {
662 case 'p':
663 algorithmName = "PlanarEmbed";
664 break;
665 case 'd':
666 algorithmName = DRAWPLANAR_NAME;
667 break;
668 case 'o':
669 algorithmName = "OuterplanarEmbed";
670 break;
671 case '2':
672 algorithmName = K23SEARCH_NAME;
673 break;
674 case '3':
675 algorithmName = K33SEARCH_NAME;
676 break;
677 case '4':
678 algorithmName = K4SEARCH_NAME;
679 break;
680 default:
681 break;
682 }
683
684 return algorithmName;
685}
686
687/****************************************************************************
688 ****************************************************************************/
689
690char const *GetTransformationName(char command)
691{
692 char const *transformationName = "UnsupportedTransformation";
693
694 switch (command)
695 {
696 case 'g':
697 transformationName = "G6";
698 break;
699 case 'a':
700 transformationName = "AdjList";
701 break;
702 case 'm':
703 transformationName = "AdjMat";
704 break;
705 default:
706 break;
707 }
708
709 return transformationName;
710}
711
712char const *GetSupportedOutputChoices(void)
713{
714 return "G. G6 format\n"
715 "A. Adjacency List format\n"
716 "M. Adjacency Matrix format\n";
717}
718
719char const *GetSupportedOutputFormats(void)
720{
721 return "gam";
722}
723
724/****************************************************************************
725 ****************************************************************************/
726
727char const *GetBaseName(int baseFlag)
728{
729 char const *transformationName = baseFlag ? "1-based" : "0-based";
730
731 return transformationName;
732}
733
734/****************************************************************************
735 * AttachAlgorithm()
736 *
737 * Determines the main graph algorithm command indicated by the command char
738 * and attaches the corresponding graph algorithm extension. The modifier is not
739 * required to determine which graph algorithm extension to attach, and is only
740 * used to signal that the behaviour of the main extension should be modified.
741 *
742 * Returns OK if graph algorithm extension corresponding to the command char
743 * was successfully attached. Returns NOTOK if theGraph is not properly
744 * initialized, if attaching the graph algorithm extension failed, or if an
745 * invalid command char was supplied.
746 ****************************************************************************/
747
748int AttachAlgorithm(graphP theGraph, char command)
749{
750 if (theGraph == NULL || theGraph->N <= 0)
751 {
752 ErrorMessage("Unable to attach graph algorithm extension to NULL or uninitialized graphP.\n");
753 return NOTOK;
754 }
755
756 switch (command)
757 {
758 case 'p':
759 // Planarity is attached by default
760 return OK;
761 case 'd':
762 return gp_AttachDrawPlanar(theGraph);
763 case 'o':
764 // Outerplanarity is attached by default
765 return OK;
766 case '2':
767 return gp_AttachK23Search(theGraph);
768 case '3':
769 return gp_AttachK33Search(theGraph);
770 case '4':
771 return gp_AttachK4Search(theGraph);
772 default:
773 break;
774 }
775
776 return NOTOK;
777}
778
779/****************************************************************************
780 A string used to construct input and output filenames.
781
782 The SUFFIXMAXLENGTH is 32 to accommodate ".out.txt" + ".render.txt" + ".test.txt"
783 ****************************************************************************/
784
785char theFileName[FILENAMEMAXLENGTH + 1 + ALGORITHMNAMEMAXLENGTH + 1 + SUFFIXMAXLENGTH + 1];
786
787/****************************************************************************
788 ConstructInputFilename()
789 Returns a string not owned by the caller (do not free string).
790 String contains infileName content if infileName is non-NULL.
791 If infileName is NULL, then the user is asked to supply a name.
792 Returns NULL on error, or a non-NULL string on success.
793 ****************************************************************************/
794
795char *ConstructInputFilename(char const *infileName)
796{
797 int Result = OK;
798
799 int numCharsToReprFILENAMEMAXLENGTH = 0;
800 char const *fileNameFormatFormat = " %%%d[^\r\n]";
801 char *fileNameFormat = NULL;
802 char lineBuff[MAXLINE + 1];
803
804 memset(lineBuff, '\0', (MAXLINE + 1));
805
806 if (GetNumCharsToReprInt(FILENAMEMAXLENGTH, &numCharsToReprFILENAMEMAXLENGTH) != OK)
807 {
808 ErrorMessage("Unable to determine number of characters required to represent FILENAMEMAXLENGTH.\n");
809 return NULL;
810 }
811
812 fileNameFormat = (char *)malloc((strlen(fileNameFormatFormat) + numCharsToReprFILENAMEMAXLENGTH + 1) * sizeof(char));
813 if (fileNameFormat == NULL)
814 {
815 ErrorMessage("Unable to allocate memory for filename format string.\n");
816 return NULL;
817 }
818
819#pragma GCC diagnostic push
820#pragma GCC diagnostic ignored "-Wformat-nonliteral"
821 sprintf(fileNameFormat, fileNameFormatFormat, FILENAMEMAXLENGTH);
822#pragma GCC diagnostic pop
823 if (infileName == NULL)
824 {
825 while (1)
826 {
827 Prompt("Enter graph file name: ");
828 if (GetLineFromStdin(lineBuff, MAXLINE) != OK)
829 {
830 ErrorMessage("Unable to read graph file name from stdin.\n");
831 Result = NOTOK;
832 break;
833 }
834
835#pragma GCC diagnostic push
836#pragma GCC diagnostic ignored "-Wformat-nonliteral"
837 if (strlen(lineBuff) == 0 || strlen(lineBuff) > FILENAMEMAXLENGTH ||
838 sscanf(lineBuff, fileNameFormat, theFileName) != 1)
839 ErrorMessage("Invalid input filename.\n");
840 else
841 {
842 if (strncmp(theFileName, "stdin", strlen("stdin")) != 0 && !strchr(theFileName, '.'))
843 {
844 Message("Graph file name does not have extension; automatically appending \".txt\".\n");
845 if (strcat(theFileName, ".txt") == NULL)
846 {
847 ErrorMessage("Appending \".txt\" extension to theFileName using strcat() failed.\n");
848 Result = NOTOK;
849 }
850 }
851 break;
852 }
853#pragma GCC diagnostic pop
854 }
855 }
856 else
857 {
858 if (strlen(infileName) > FILENAMEMAXLENGTH)
859 {
860 ErrorMessage("Filename is too long.\n");
861 Result = NOTOK;
862 }
863 else if (strlen(infileName) == 0)
864 {
865 ErrorMessage("Filename is empty.\n");
866 Result = NOTOK;
867 }
868
869 if (Result == OK)
870 {
871 if (strcpy(theFileName, infileName) == NULL)
872 {
873 ErrorMessage("Copying infileName into theFileName using strcpy() failed.\n");
874 Result = NOTOK;
875 }
876 }
877 }
878
879 if (fileNameFormat != NULL)
880 {
881 free(fileNameFormat);
882 fileNameFormat = NULL;
883 }
884
885 return Result != OK ? NULL : theFileName;
886}
887
888/****************************************************************************
889 * ConstructPrimaryOutputFilename()
890 *
891 * Returns a string not owned by the caller (do not free string).
892 * Reuses the same memory space as ConstructInputFilename().
893 * If outfileName is non-NULL, then the result string contains its content.
894 * If outfileName is NULL, then the infileName and the command's algorithm name
895 * are used to construct a string.
896 *
897 * Returns non-NULL string
898 ****************************************************************************/
899
900char *ConstructPrimaryOutputFilename(char const *infileName, char const *outfileName, char command)
901{
902 char const *algorithmName = GetAlgorithmName(command);
903
904 if (outfileName == NULL)
905 {
906 // The output filename is based on the input filename
907 if (theFileName != infileName)
908 strcpy(theFileName, infileName);
909
910 // If the primary output filename has not been given, then we use
911 // the input filename + the algorithm name + a simple suffix
912 if (strlen(algorithmName) <= ALGORITHMNAMEMAXLENGTH)
913 {
914 strcat(theFileName, ".");
915 strcat(theFileName, algorithmName);
916 }
917 else
918 ErrorMessage("Algorithm Name is too long, so it will not be used in output filename.\n");
919
920 strcat(theFileName, ".out.txt");
921 }
922 else
923 {
924 if (strlen(outfileName) > FILENAMEMAXLENGTH)
925 {
926 char const *messageFormat = "Outfile filename is too long. Result placed in \"%.*s\"";
927 int charsAvailForStrToInclude = (int)(MAXLINE - strlen(messageFormat));
928 char messageContents[MAXLINE + 1];
929 messageContents[0] = '\0';
930
931 // The output filename is based on the input filename
932 if (theFileName != infileName)
933 strcpy(theFileName, infileName);
934
935 if (strlen(algorithmName) <= ALGORITHMNAMEMAXLENGTH)
936 {
937 strcat(theFileName, ".");
938 strcat(theFileName, algorithmName);
939 }
940 strcat(theFileName, ".out.txt");
941
942#pragma GCC diagnostic push
943#pragma GCC diagnostic ignored "-Wformat-nonliteral"
944 sprintf(messageContents, messageFormat, charsAvailForStrToInclude, theFileName);
945#pragma GCC diagnostic pop
946 ErrorMessage(messageContents);
947 }
948 else
949 {
950 if (theFileName != outfileName)
951 strcpy(theFileName, outfileName);
952 }
953 }
954
955 return theFileName;
956}
957
958/****************************************************************************
959 * ConstructTransformationExpectedResultFilename()
960 *
961 * Returns a string whose ownership will be transferred to the caller (must free
962 * string).
963 * If outfileName is non-NULL, then the result string contains its content
964 * If outfileName is NULL, then the infileName, the command's algorithm name,
965 * and whether or not this output file correspond to the actual (0) or expected
966 * (1) output file from testing are used to construct a string.
967 *
968 * Returns non-NULL string
969 ****************************************************************************/
970
971int ConstructTransformationExpectedResultFilename(char const *infileName, char **outfileName, char command, int baseFlag)
972{
973 int Result = OK;
974
975 char const *baseName = GetBaseName(baseFlag);
976 char const *transformationName = GetTransformationName(command);
977 int infileNameLen = -1;
978
979 if (infileName == NULL || (infileNameLen = strlen(infileName)) < 1)
980 {
981 ErrorMessage("Cannot construct transformation output filename for empty infileName.\n");
982 return NOTOK;
983 }
984
985 if ((*outfileName) == NULL)
986 {
987 (*outfileName) = (char *)calloc(
988 infileNameLen + 1 + strlen(baseName) + 1 + strlen(transformationName) +
989 ((command == 'g') ? strlen(".out.g6") : strlen(".out.txt")) + 1,
990 sizeof(char));
991
992 if ((*outfileName) == NULL)
993 {
994 ErrorMessage("Unable to allocate memory for output filename.\n");
995 return NOTOK;
996 }
997
998 strcpy((*outfileName), infileName);
999 strcat((*outfileName), ".");
1000 strcat((*outfileName), baseName);
1001 strcat((*outfileName), ".");
1002 strcat((*outfileName), transformationName);
1003 strcat((*outfileName), command == 'g' ? ".out.g6" : ".out.txt");
1004 }
1005 else
1006 {
1007 ErrorMessage("outfileName already allocated.\n");
1008 Result = NOTOK;
1009 }
1010
1011 return Result;
1012}
1013
1014/****************************************************************************
1015 * WriteAlgorithmResults()
1016 ****************************************************************************/
1017
1018void WriteAlgorithmResults(graphP theGraph, int Result, char command, platform_time start, platform_time end, char const *infileName)
1019{
1020 char const *messageFormat = NULL;
1021 char messageContents[MAXLINE + 1];
1022 int charsAvailForStr = 0;
1023
1024 memset(messageContents, '\0', (MAXLINE + 1));
1025
1026 if (infileName)
1027 {
1028 messageFormat = "The graph \"%.*s\" ";
1029 charsAvailForStr = (int)(MAXLINE - strlen(messageFormat));
1030#pragma GCC diagnostic push
1031#pragma GCC diagnostic ignored "-Wformat-nonliteral"
1032 sprintf(messageContents, messageFormat, charsAvailForStr, infileName);
1033#pragma GCC diagnostic pop
1034 }
1035 else
1036 sprintf(messageContents, "The graph ");
1037 Message(messageContents);
1038
1039 switch (command)
1040 {
1041 case 'p':
1042 sprintf(messageContents, "is%s planar.\n", Result == OK ? "" : " not");
1043 break;
1044 case 'd':
1045 sprintf(messageContents, "is%s planar.\n", Result == OK ? "" : " not");
1046 break;
1047 case 'o':
1048 sprintf(messageContents, "is%s outerplanar.\n", Result == OK ? "" : " not");
1049 break;
1050 case '2':
1051 sprintf(messageContents, "has %s subgraph homeomorphic to K_{2,3}.\n", Result == OK ? "no" : "a");
1052 break;
1053 case '3':
1054 sprintf(messageContents, "has %s subgraph homeomorphic to K_{3,3}.\n", Result == OK ? "no" : "a");
1055 break;
1056 case '4':
1057 sprintf(messageContents, "has %s subgraph homeomorphic to K_4.\n", Result == OK ? "no" : "a");
1058 break;
1059 default:
1060 sprintf(messageContents, "has not been processed due to unrecognized command.\n");
1061 break;
1062 }
1063 Message(messageContents);
1064
1065 sprintf(messageContents, "Algorithm '%s' executed in %.3lf seconds.\n",
1066 GetAlgorithmName(command), platform_GetDuration(start, end));
1067 Message(messageContents);
1068}
1069

Before

translation_unit
comment "/*\nCopyright (c) 1997-2025, John M. Boyer\nAll rights reserve"...
preproc_include
#include "#include"
string_literal
" "\""
string_content "planarity.h"
" "\""
comment "/***********************************************************"...
declaration (+50 nodes collapsed)
function_definition (+298 nodes collapsed)
function_definition (+23 nodes collapsed)
function_definition (+33 nodes collapsed)
comment "/***********************************************************"...
function_definition (+328 nodes collapsed)
comment "/***********************************************************"...
function_definition (+231 nodes collapsed)
comment "/***********************************************************"...
function_definition (+296 nodes collapsed)
comment "/***********************************************************"...
function_definition (+330 nodes collapsed)
comment "/***********************************************************"...
function_definition (+273 nodes collapsed)
comment "/***********************************************************"...
function_definition (+59 nodes collapsed)
function_definition (+50 nodes collapsed)
function_definition (+23 nodes collapsed)
comment "/***********************************************************"...
function_definition (+135 nodes collapsed)
comment "/***********************************************************"...
function_definition (+152 nodes collapsed)
comment "/***********************************************************"...
function_definition (+107 nodes collapsed)
function_definition (+35 nodes collapsed)
function_definition (+23 nodes collapsed)
comment "/***********************************************************"...
function_definition (+43 nodes collapsed)
comment "/***********************************************************"...
function_definition (+104 nodes collapsed)
comment "/***********************************************************"...
preproc_def
#define "#define"
identifier "FILENAMEMAXLENGTH"
preproc_arg "128"
preproc_def
#define "#define"
identifier "ALGORITHMNAMEMAXLENGTH"
preproc_arg "32"
preproc_def
#define "#define"
identifier "SUFFIXMAXLENGTH"
preproc_arg "32"
declaration (+23 nodes collapsed)
comment "/***********************************************************"...
function_definition (+246 nodes collapsed)
comment "/***********************************************************"...
function_definition (+366 nodes collapsed)
comment "/***********************************************************"...
function_definition (+408 nodes collapsed)

After

translation_unit
comment "/*\nCopyright (c) 1997-2025, John M. Boyer\nAll rights reserve"...
preproc_include
#include "#include"
string_literal
" "\""
string_content "planarity.h"
" "\""
comment "/***********************************************************"...
declaration (+50 nodes collapsed)
function_definition (+1042 nodes collapsed)
function_definition (+143 nodes collapsed)
function_definition (+37 nodes collapsed)
function_definition (+33 nodes collapsed)
comment "/***********************************************************"...
function_definition (+328 nodes collapsed)
comment "/***********************************************************"...
function_definition (+231 nodes collapsed)
comment "/***********************************************************"...
function_definition (+296 nodes collapsed)
comment "/***********************************************************"...
function_definition (+330 nodes collapsed)
comment "/***********************************************************"...
function_definition (+273 nodes collapsed)
comment "/***********************************************************"...
function_definition (+59 nodes collapsed)
function_definition (+50 nodes collapsed)
function_definition (+23 nodes collapsed)
comment "/***********************************************************"...
function_definition (+289 nodes collapsed)
comment "/***********************************************************"...
function_definition (+249 nodes collapsed)
comment "/***********************************************************"...
function_definition (+152 nodes collapsed)
comment "/***********************************************************"...
function_definition (+107 nodes collapsed)
function_definition (+35 nodes collapsed)
function_definition (+23 nodes collapsed)
comment "/***********************************************************"...
function_definition (+43 nodes collapsed)
comment "/***********************************************************"...
function_definition (+162 nodes collapsed)
comment "/***********************************************************"...
declaration (+23 nodes collapsed)
comment "/***********************************************************"...
function_definition (+639 nodes collapsed)
comment "/***********************************************************"...
function_definition (+367 nodes collapsed)
comment "/***********************************************************"...
function_definition (+408 nodes collapsed)
comment "/***********************************************************"...
function_definition (+490 nodes collapsed)