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