1 /*
2 * File/directory test program for CUPS.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright © 2007-2018 by Apple Inc.
6 * Copyright © 1997-2007 by Easy Software Products.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * information.
10 */
11
12 /*
13 * Include necessary headers...
14 */
15
16 #include "string-private.h"
17 #include "debug-private.h"
18 #include "file.h"
19 #include "dir.h"
20 #include <stdlib.h>
21 #include <time.h>
22 #ifdef _WIN32
23 # include <io.h>
24 #else
25 # include <unistd.h>
26 #endif /* _WIN32 */
27 #include <fcntl.h>
28
29
30 /*
31 * Local functions...
32 */
33
34 static int count_lines(cups_file_t *fp);
35 static int random_tests(void);
36 static int read_write_tests(int compression);
37
38
39 /*
40 * 'main()' - Main entry.
41 */
42
43 int /* O - Exit status */
main(int argc,char * argv[])44 main(int argc, /* I - Number of command-line arguments */
45 char *argv[]) /* I - Command-line arguments */
46 {
47 int status; /* Exit status */
48 int i; /* Looping var */
49 char filename[1024]; /* Filename buffer */
50 cups_file_t *fp; /* File pointer */
51 #ifndef _WIN32
52 int fds[2]; /* Open file descriptors */
53 cups_file_t *fdfile; /* File opened with cupsFileOpenFd() */
54 #endif /* !_WIN32 */
55 int count; /* Number of lines in file */
56
57
58 if (argc == 1)
59 {
60 /*
61 * Do uncompressed file tests...
62 */
63
64 status = read_write_tests(0);
65
66 #ifdef HAVE_LIBZ
67 /*
68 * Do compressed file tests...
69 */
70
71 putchar('\n');
72
73 status += read_write_tests(1);
74 #endif /* HAVE_LIBZ */
75
76 /*
77 * Do uncompressed random I/O tests...
78 */
79
80 status += random_tests();
81
82 #ifndef _WIN32
83 /*
84 * Test fdopen and close without reading...
85 */
86
87 pipe(fds);
88 close(fds[1]);
89
90 fputs("\ncupsFileOpenFd(fd, \"r\"): ", stdout);
91 fflush(stdout);
92
93 if ((fdfile = cupsFileOpenFd(fds[0], "r")) == NULL)
94 {
95 puts("FAIL");
96 status ++;
97 }
98 else
99 {
100 /*
101 * Able to open file, now close without reading. If we don't return
102 * before the alarm fires, that is a failure and we will crash on the
103 * alarm signal...
104 */
105
106 puts("PASS");
107 fputs("cupsFileClose(no read): ", stdout);
108 fflush(stdout);
109
110 alarm(5);
111 cupsFileClose(fdfile);
112 alarm(0);
113
114 puts("PASS");
115 }
116 #endif /* !_WIN32 */
117
118 /*
119 * Count lines in test file, rewind, then count again.
120 */
121
122 fputs("\ncupsFileOpen(\"testfile.txt\", \"r\"): ", stdout);
123
124 if ((fp = cupsFileOpen("testfile.txt", "r")) == NULL)
125 {
126 puts("FAIL");
127 status ++;
128 }
129 else
130 {
131 puts("PASS");
132 fputs("cupsFileGets: ", stdout);
133
134 if ((count = count_lines(fp)) != 477)
135 {
136 printf("FAIL (got %d lines, expected 477)\n", count);
137 status ++;
138 }
139 else
140 {
141 puts("PASS");
142 fputs("cupsFileRewind: ", stdout);
143
144 if (cupsFileRewind(fp) != 0)
145 {
146 puts("FAIL");
147 status ++;
148 }
149 else
150 {
151 puts("PASS");
152 fputs("cupsFileGets: ", stdout);
153
154 if ((count = count_lines(fp)) != 477)
155 {
156 printf("FAIL (got %d lines, expected 477)\n", count);
157 status ++;
158 }
159 else
160 puts("PASS");
161 }
162 }
163
164 cupsFileClose(fp);
165 }
166
167 /*
168 * Test path functions...
169 */
170
171 fputs("\ncupsFileFind: ", stdout);
172 #ifdef _WIN32
173 if (cupsFileFind("notepad.exe", "C:/WINDOWS", 1, filename, sizeof(filename)) &&
174 cupsFileFind("notepad.exe", "C:/WINDOWS;C:/WINDOWS/SYSTEM32", 1, filename, sizeof(filename)))
175 #else
176 if (cupsFileFind("cat", "/bin", 1, filename, sizeof(filename)) &&
177 cupsFileFind("cat", "/bin:/usr/bin", 1, filename, sizeof(filename)))
178 #endif /* _WIN32 */
179 printf("PASS (%s)\n", filename);
180 else
181 {
182 puts("FAIL");
183 status ++;
184 }
185
186 /*
187 * Test directory functions...
188 */
189
190 fputs("\nCreating test directory \"test.d\"...\n", stdout);
191 fputs("mkdir(test.d): ", stdout);
192 if (mkdir("test.d", 0777))
193 {
194 printf("FAIL (%s)\n", strerror(errno));
195 status ++;
196 }
197 else
198 {
199 int num_files; // Number of files seen
200 cups_dir_t *dir; // Directory pointer
201 cups_dentry_t *dent; // Directory entry
202
203 puts("PASS");
204
205 fputs("cupsDirOpen(test.d): ", stdout);
206 if ((dir = cupsDirOpen("test.d")) == NULL)
207 {
208 printf("FAIL (%s)\n", strerror(errno));
209 status ++;
210 }
211 else
212 {
213 puts("PASS");
214 fputs("cupsDirRead: ", stdout);
215 if ((dent = cupsDirRead(dir)) != NULL)
216 {
217 printf("FAIL (Got '%s', expected NULL)\n", dent->filename);
218 status ++;
219 }
220 else
221 puts("PASS");
222
223 cupsDirClose(dir);
224 }
225
226 // Create some files...
227 for (i = 0; i < 10; i ++)
228 {
229 snprintf(filename, sizeof(filename), "test.d/testfile%d.txt", i);
230 printf("cupsFileOpen(%s): ", filename);
231 if ((fp = cupsFileOpen(filename, "w")) == NULL)
232 {
233 printf("FAIL (%s)\n", strerror(errno));
234 status ++;
235 break;
236 }
237 else
238 {
239 puts("PASS");
240 cupsFilePuts(fp, "This is a test.\n");
241 cupsFileClose(fp);
242 }
243 }
244
245 if (i >= 10)
246 {
247 fputs("cupsDirOpen(test.d): ", stdout);
248 if ((dir = cupsDirOpen("test.d")) == NULL)
249 {
250 printf("FAIL (%s)\n", strerror(errno));
251 status ++;
252 }
253 else
254 {
255 puts("PASS");
256 fputs("cupsDirRead: ", stdout);
257 for (num_files = 0; (dent = cupsDirRead(dir)) != NULL; num_files ++)
258 printf("%s ", dent->filename);
259 if (num_files != 10)
260 {
261 printf("FAIL (Got %d files, expected 10)\n", num_files);
262 status ++;
263 }
264 else
265 puts("PASS");
266
267 cupsDirClose(dir);
268 }
269 }
270
271 // Cleanup
272 for (i = 0; i < 10; i ++)
273 {
274 snprintf(filename, sizeof(filename), "test.d/testfile%d.txt", i);
275 unlink(filename);
276 }
277
278 rmdir("test.d");
279 }
280
281 /*
282 * Summarize the results and return...
283 */
284
285 if (!status)
286 puts("\nALL TESTS PASSED!");
287 else
288 printf("\n%d TEST(S) FAILED!\n", status);
289 }
290 else
291 {
292 /*
293 * Cat the filename on the command-line...
294 */
295
296 char line[8192]; /* Line from file */
297
298 if ((fp = cupsFileOpen(argv[1], "r")) == NULL)
299 {
300 perror(argv[1]);
301 status = 1;
302 }
303 else if (argc == 2)
304 {
305 status = 0;
306
307 while (cupsFileGets(fp, line, sizeof(line)))
308 puts(line);
309
310 if (!cupsFileEOF(fp))
311 perror(argv[1]);
312
313 cupsFileClose(fp);
314 }
315 else
316 {
317 status = 0;
318 ssize_t bytes;
319
320 while ((bytes = cupsFileRead(fp, line, sizeof(line))) > 0)
321 printf("%s: %d bytes\n", argv[1], (int)bytes);
322
323 if (cupsFileEOF(fp))
324 printf("%s: EOF\n", argv[1]);
325 else
326 perror(argv[1]);
327
328 cupsFileClose(fp);
329 }
330 }
331
332 return (status);
333 }
334
335
336 /*
337 * 'count_lines()' - Count the number of lines in a file.
338 */
339
340 static int /* O - Number of lines */
count_lines(cups_file_t * fp)341 count_lines(cups_file_t *fp) /* I - File to read from */
342 {
343 int count; /* Number of lines */
344 char line[1024]; /* Line buffer */
345
346
347 for (count = 0; cupsFileGets(fp, line, sizeof(line)); count ++);
348
349 return (count);
350 }
351
352
353 /*
354 * 'random_tests()' - Do random access tests.
355 */
356
357 static int /* O - Status */
random_tests(void)358 random_tests(void)
359 {
360 int status, /* Status of tests */
361 pass, /* Current pass */
362 count, /* Number of records read */
363 record, /* Current record */
364 num_records; /* Number of records */
365 off_t pos; /* Position in file */
366 ssize_t expected; /* Expected position in file */
367 cups_file_t *fp; /* File */
368 char buffer[512]; /* Data buffer */
369
370
371 /*
372 * Run 4 passes, each time appending to a data file and then reopening the
373 * file for reading to validate random records in the file.
374 */
375
376 for (status = 0, pass = 0; pass < 4; pass ++)
377 {
378 /*
379 * cupsFileOpen(append)
380 */
381
382 printf("\ncupsFileOpen(append %d): ", pass);
383
384 if ((fp = cupsFileOpen("testfile.dat", "a")) == NULL)
385 {
386 printf("FAIL (%s)\n", strerror(errno));
387 status ++;
388 break;
389 }
390 else
391 puts("PASS");
392
393 /*
394 * cupsFileTell()
395 */
396
397 expected = 256 * (ssize_t)sizeof(buffer) * pass;
398
399 fputs("cupsFileTell(): ", stdout);
400 if ((pos = cupsFileTell(fp)) != (off_t)expected)
401 {
402 printf("FAIL (" CUPS_LLFMT " instead of " CUPS_LLFMT ")\n",
403 CUPS_LLCAST pos, CUPS_LLCAST expected);
404 status ++;
405 break;
406 }
407 else
408 puts("PASS");
409
410 /*
411 * cupsFileWrite()
412 */
413
414 fputs("cupsFileWrite(256 512-byte records): ", stdout);
415 for (record = 0; record < 256; record ++)
416 {
417 memset(buffer, record, sizeof(buffer));
418 if (cupsFileWrite(fp, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer))
419 break;
420 }
421
422 if (record < 256)
423 {
424 printf("FAIL (%d: %s)\n", record, strerror(errno));
425 status ++;
426 break;
427 }
428 else
429 puts("PASS");
430
431 /*
432 * cupsFileTell()
433 */
434
435 expected += 256 * (ssize_t)sizeof(buffer);
436
437 fputs("cupsFileTell(): ", stdout);
438 if ((pos = cupsFileTell(fp)) != (off_t)expected)
439 {
440 printf("FAIL (" CUPS_LLFMT " instead of " CUPS_LLFMT ")\n",
441 CUPS_LLCAST pos, CUPS_LLCAST expected);
442 status ++;
443 break;
444 }
445 else
446 puts("PASS");
447
448 cupsFileClose(fp);
449
450 /*
451 * cupsFileOpen(read)
452 */
453
454 printf("\ncupsFileOpen(read %d): ", pass);
455
456 if ((fp = cupsFileOpen("testfile.dat", "r")) == NULL)
457 {
458 printf("FAIL (%s)\n", strerror(errno));
459 status ++;
460 break;
461 }
462 else
463 puts("PASS");
464
465 /*
466 * cupsFileSeek, cupsFileRead
467 */
468
469 fputs("cupsFileSeek(), cupsFileRead(): ", stdout);
470
471 for (num_records = (pass + 1) * 256, count = (pass + 1) * 256, record = ((int)CUPS_RAND() & 65535) % num_records;
472 count > 0;
473 count --, record = (record + ((int)CUPS_RAND() & 31) - 16 + num_records) % num_records)
474 {
475 /*
476 * The last record is always the first...
477 */
478
479 if (count == 1)
480 record = 0;
481
482 /*
483 * Try reading the data for the specified record, and validate the
484 * contents...
485 */
486
487 expected = (ssize_t)sizeof(buffer) * record;
488
489 if ((pos = cupsFileSeek(fp, expected)) != expected)
490 {
491 printf("FAIL (" CUPS_LLFMT " instead of " CUPS_LLFMT ")\n",
492 CUPS_LLCAST pos, CUPS_LLCAST expected);
493 status ++;
494 break;
495 }
496 else
497 {
498 if (cupsFileRead(fp, buffer, sizeof(buffer)) != sizeof(buffer))
499 {
500 printf("FAIL (%s)\n", strerror(errno));
501 status ++;
502 break;
503 }
504 else if ((buffer[0] & 255) != (record & 255) ||
505 memcmp(buffer, buffer + 1, sizeof(buffer) - 1))
506 {
507 printf("FAIL (Bad Data - %d instead of %d)\n", buffer[0] & 255,
508 record & 255);
509 status ++;
510 break;
511 }
512 }
513 }
514
515 if (count == 0)
516 puts("PASS");
517
518 cupsFileClose(fp);
519 }
520
521 /*
522 * Remove the test file...
523 */
524
525 unlink("testfile.dat");
526
527 /*
528 * Return the test status...
529 */
530
531 return (status);
532 }
533
534
535 /*
536 * 'read_write_tests()' - Perform read/write tests.
537 */
538
539 static int /* O - Status */
read_write_tests(int compression)540 read_write_tests(int compression) /* I - Use compression? */
541 {
542 int i; /* Looping var */
543 cups_file_t *fp; /* File */
544 int status; /* Exit status */
545 char line[1024], /* Line from file */
546 *value; /* Directive value from line */
547 int linenum; /* Line number */
548 unsigned char readbuf[8192], /* Read buffer */
549 writebuf[8192]; /* Write buffer */
550 int byte; /* Byte from file */
551 ssize_t bytes; /* Number of bytes read/written */
552 off_t length; /* Length of file */
553 static const char *partial_line = "partial line";
554 /* Partial line */
555
556
557 /*
558 * No errors so far...
559 */
560
561 status = 0;
562
563 /*
564 * Initialize the write buffer with random data...
565 */
566
567 CUPS_SRAND((unsigned)time(NULL));
568
569 for (i = 0; i < (int)sizeof(writebuf); i ++)
570 writebuf[i] = (unsigned char)CUPS_RAND();
571
572 /*
573 * cupsFileOpen(write)
574 */
575
576 printf("cupsFileOpen(write%s): ", compression ? " compressed" : "");
577
578 fp = cupsFileOpen(compression ? "testfile.dat.gz" : "testfile.dat",
579 compression ? "w9" : "w");
580 if (fp)
581 {
582 puts("PASS");
583
584 /*
585 * cupsFileCompression()
586 */
587
588 fputs("cupsFileCompression(): ", stdout);
589
590 if (cupsFileCompression(fp) == compression)
591 puts("PASS");
592 else
593 {
594 printf("FAIL (Got %d, expected %d)\n", cupsFileCompression(fp),
595 compression);
596 status ++;
597 }
598
599 /*
600 * cupsFilePuts()
601 */
602
603 fputs("cupsFilePuts(): ", stdout);
604
605 if (cupsFilePuts(fp, "# Hello, World\n") > 0)
606 puts("PASS");
607 else
608 {
609 printf("FAIL (%s)\n", strerror(errno));
610 status ++;
611 }
612
613 /*
614 * cupsFilePrintf()
615 */
616
617 fputs("cupsFilePrintf(): ", stdout);
618
619 for (i = 0; i < 1000; i ++)
620 if (cupsFilePrintf(fp, "TestLine %03d\n", i) < 0)
621 break;
622
623 if (i >= 1000)
624 puts("PASS");
625 else
626 {
627 printf("FAIL (%s)\n", strerror(errno));
628 status ++;
629 }
630
631 /*
632 * cupsFilePutChar()
633 */
634
635 fputs("cupsFilePutChar(): ", stdout);
636
637 for (i = 0; i < 256; i ++)
638 if (cupsFilePutChar(fp, i) < 0)
639 break;
640
641 if (i >= 256)
642 puts("PASS");
643 else
644 {
645 printf("FAIL (%s)\n", strerror(errno));
646 status ++;
647 }
648
649 /*
650 * cupsFileWrite()
651 */
652
653 fputs("cupsFileWrite(): ", stdout);
654
655 for (i = 0; i < 10000; i ++)
656 if (cupsFileWrite(fp, (char *)writebuf, sizeof(writebuf)) < 0)
657 break;
658
659 if (i >= 10000)
660 puts("PASS");
661 else
662 {
663 printf("FAIL (%s)\n", strerror(errno));
664 status ++;
665 }
666
667 /*
668 * cupsFilePuts() with partial line...
669 */
670
671 fputs("cupsFilePuts(\"partial line\"): ", stdout);
672
673 if (cupsFilePuts(fp, partial_line) > 0)
674 puts("PASS");
675 else
676 {
677 printf("FAIL (%s)\n", strerror(errno));
678 status ++;
679 }
680
681 /*
682 * cupsFileTell()
683 */
684
685 fputs("cupsFileTell(): ", stdout);
686
687 if ((length = cupsFileTell(fp)) == 81933283)
688 puts("PASS");
689 else
690 {
691 printf("FAIL (" CUPS_LLFMT " instead of 81933283)\n", CUPS_LLCAST length);
692 status ++;
693 }
694
695 /*
696 * cupsFileClose()
697 */
698
699 fputs("cupsFileClose(): ", stdout);
700
701 if (!cupsFileClose(fp))
702 puts("PASS");
703 else
704 {
705 printf("FAIL (%s)\n", strerror(errno));
706 status ++;
707 }
708 }
709 else
710 {
711 printf("FAIL (%s)\n", strerror(errno));
712 status ++;
713 }
714
715 /*
716 * cupsFileOpen(read)
717 */
718
719 fputs("\ncupsFileOpen(read): ", stdout);
720
721 fp = cupsFileOpen(compression ? "testfile.dat.gz" : "testfile.dat", "r");
722 if (fp)
723 {
724 puts("PASS");
725
726 /*
727 * cupsFileGets()
728 */
729
730 fputs("cupsFileGets(): ", stdout);
731
732 if (cupsFileGets(fp, line, sizeof(line)))
733 {
734 if (line[0] == '#')
735 puts("PASS");
736 else
737 {
738 printf("FAIL (Got line \"%s\", expected comment line)\n", line);
739 status ++;
740 }
741 }
742 else
743 {
744 printf("FAIL (%s)\n", strerror(errno));
745 status ++;
746 }
747
748 /*
749 * cupsFileCompression()
750 */
751
752 fputs("cupsFileCompression(): ", stdout);
753
754 if (cupsFileCompression(fp) == compression)
755 puts("PASS");
756 else
757 {
758 printf("FAIL (Got %d, expected %d)\n", cupsFileCompression(fp),
759 compression);
760 status ++;
761 }
762
763 /*
764 * cupsFileGetConf()
765 */
766
767 linenum = 1;
768
769 fputs("cupsFileGetConf(): ", stdout);
770
771 for (i = 0, value = NULL; i < 1000; i ++)
772 if (!cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
773 break;
774 else if (_cups_strcasecmp(line, "TestLine") || !value || atoi(value) != i ||
775 linenum != (i + 2))
776 break;
777
778 if (i >= 1000)
779 puts("PASS");
780 else if (line[0])
781 {
782 printf("FAIL (Line %d, directive \"%s\", value \"%s\")\n", linenum,
783 line, value ? value : "(null)");
784 status ++;
785 }
786 else
787 {
788 printf("FAIL (%s)\n", strerror(errno));
789 status ++;
790 }
791
792 /*
793 * cupsFileGetChar()
794 */
795
796 fputs("cupsFileGetChar(): ", stdout);
797
798 for (i = 0, byte = 0; i < 256; i ++)
799 if ((byte = cupsFileGetChar(fp)) != i)
800 break;
801
802 if (i >= 256)
803 puts("PASS");
804 else if (byte >= 0)
805 {
806 printf("FAIL (Got %d, expected %d)\n", byte, i);
807 status ++;
808 }
809 else
810 {
811 printf("FAIL (%s)\n", strerror(errno));
812 status ++;
813 }
814
815 /*
816 * cupsFileRead()
817 */
818
819 fputs("cupsFileRead(): ", stdout);
820
821 for (i = 0, bytes = 0; i < 10000; i ++)
822 if ((bytes = cupsFileRead(fp, (char *)readbuf, sizeof(readbuf))) < 0)
823 break;
824 else if (memcmp(readbuf, writebuf, sizeof(readbuf)))
825 break;
826
827 if (i >= 10000)
828 puts("PASS");
829 else if (bytes > 0)
830 {
831 printf("FAIL (Pass %d, ", i);
832
833 for (i = 0; i < (int)sizeof(readbuf); i ++)
834 if (readbuf[i] != writebuf[i])
835 break;
836
837 printf("match failed at offset %d - got %02X, expected %02X)\n",
838 i, readbuf[i], writebuf[i]);
839 }
840 else
841 {
842 printf("FAIL (%s)\n", strerror(errno));
843 status ++;
844 }
845
846 /*
847 * cupsFileGetChar() with partial line...
848 */
849
850 fputs("cupsFileGetChar(partial line): ", stdout);
851
852 for (i = 0; i < (int)strlen(partial_line); i ++)
853 if ((byte = cupsFileGetChar(fp)) < 0)
854 break;
855 else if (byte != partial_line[i])
856 break;
857
858 if (!partial_line[i])
859 puts("PASS");
860 else
861 {
862 printf("FAIL (got '%c', expected '%c')\n", byte, partial_line[i]);
863 status ++;
864 }
865
866 /*
867 * cupsFileTell()
868 */
869
870 fputs("cupsFileTell(): ", stdout);
871
872 if ((length = cupsFileTell(fp)) == 81933283)
873 puts("PASS");
874 else
875 {
876 printf("FAIL (" CUPS_LLFMT " instead of 81933283)\n", CUPS_LLCAST length);
877 status ++;
878 }
879
880 /*
881 * cupsFileClose()
882 */
883
884 fputs("cupsFileClose(): ", stdout);
885
886 if (!cupsFileClose(fp))
887 puts("PASS");
888 else
889 {
890 printf("FAIL (%s)\n", strerror(errno));
891 status ++;
892 }
893 }
894 else
895 {
896 printf("FAIL (%s)\n", strerror(errno));
897 status ++;
898 }
899
900 /*
901 * Remove the test file...
902 */
903
904 if (!status)
905 unlink(compression ? "testfile.dat.gz" : "testfile.dat");
906
907 /*
908 * Return the test status...
909 */
910
911 return (status);
912 }
913