1 // RUN: %clangxx -g %s -o %t && %run %t | FileCheck %s
2 
3 // CHECK: READ CALLED; len={{[0-9]*}}
4 // CHECK-NEXT: READ: test
5 // CHECK-NEXT: WRITE CALLED: test
6 // CHECK-NEXT: READ CALLED; len={{[0-9]*}}
7 // CHECK-NEXT: READ: test
8 // CHECK-NEXT: WRITE CALLED: test
9 // CHECK-NEXT: CLOSE CALLED
10 // CHECK-NEXT: SEEK CALLED; off=100, whence=0
11 // CHECK-NEXT: READ CALLED; len={{[0-9]*}}
12 // CHECK-NEXT: READ: test
13 // CHECK-NEXT: WRITE CALLED: test
14 // CHECK-NEXT: FLUSH CALLED
15 // CHECK-NEXT: WRITE CALLED: test
16 // CHECK-NEXT: FLUSH CALLED
17 
18 #include <assert.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 int cookie_var;
24 
f_read(void * cookie,void * buf,size_t len)25 ssize_t f_read(void *cookie, void *buf, size_t len) {
26   assert(cookie == &cookie_var);
27   assert(len >= 6);
28   printf("READ CALLED; len=%zd\n", len);
29   return strlcpy((char*)buf, "test\n", len);
30 }
31 
f_write(void * cookie,const void * buf,size_t len)32 ssize_t f_write(void *cookie, const void *buf, size_t len) {
33   assert(cookie == &cookie_var);
34   char *data = strndup((char*)buf, len);
35   assert(data);
36   printf("WRITE CALLED: %s\n", data);
37   free(data);
38   return len;
39 }
40 
f_seek(void * cookie,off_t off,int whence)41 off_t f_seek(void *cookie, off_t off, int whence) {
42   assert(cookie == &cookie_var);
43   assert(whence == SEEK_SET);
44   printf("SEEK CALLED; off=%d, whence=%d\n", (int)off, whence);
45   return off;
46 }
47 
f_flush(void * cookie)48 int f_flush(void *cookie) {
49   assert(cookie == &cookie_var);
50   printf("FLUSH CALLED\n");
51   return 0;
52 }
53 
f_close(void * cookie)54 int f_close(void *cookie) {
55   assert(cookie == &cookie_var);
56   printf("CLOSE CALLED\n");
57   return 0;
58 }
59 
main(void)60 int main(void) {
61   FILE *fp;
62   char buf[10];
63 
64   // 1. read-only variant
65   fp = fropen2(&cookie_var, f_read);
66   assert(fp);
67   // verify that fileno() does not crash or report nonsense
68   assert(fileno(fp) == -1);
69   assert(fgets(buf, sizeof(buf), fp));
70   printf("READ: %s", buf);
71   assert(!fclose(fp));
72 
73   // 2. write-only variant
74   fp = fwopen2(&cookie_var, f_write);
75   assert(fp);
76   assert(fileno(fp) == -1);
77   assert(fputs("test", fp) >= 0);
78   assert(!fclose(fp));
79 
80   // 3. read+write+close
81   fp = funopen2(&cookie_var, f_read, f_write, NULL, NULL, f_close);
82   assert(fp);
83   assert(fileno(fp) == -1);
84   assert(fgets(buf, sizeof(buf), fp));
85   printf("READ: %s", buf);
86   assert(fputs("test", fp) >= 0);
87   assert(!fflush(fp));
88   assert(!fclose(fp));
89 
90   // 4. read+seek
91   fp = funopen2(&cookie_var, f_read, NULL, f_seek, NULL, NULL);
92   assert(fp);
93   assert(fileno(fp) == -1);
94   assert(fseek(fp, 100, SEEK_SET) == 0);
95   assert(fgets(buf, sizeof(buf), fp));
96   printf("READ: %s", buf);
97   assert(!fclose(fp));
98 
99   // 5. write+flush
100   fp = funopen2(&cookie_var, NULL, f_write, NULL, f_flush, NULL);
101   assert(fp);
102   assert(fileno(fp) == -1);
103   assert(fputs("test", fp) >= 0);
104   assert(!fflush(fp));
105   assert(fputs("test", fp) >= 0);
106   // NB: fclose() also implicitly calls flush
107   assert(!fclose(fp));
108 
109   return 0;
110 }
111