source: rtems/testsuites/psxtests/psxfile01/test.c @ 4af18b3

Last change on this file since 4af18b3 was 4af18b3, checked in by Sebastian Huber <sebastian.huber@…>, on Oct 11, 2018 at 8:51:21 AM

Support O_DIRECTORY open() flag

Close #3545.

  • Property mode set to 100644
File size: 18.3 KB
Line 
1/**
2 *  @file
3 *
4 *  Simple test program to exercise some of the basic functionality of
5 *  POSIX Files and Directories Support.
6 *
7 *  This test assumes that the file system is initialized with the
8 *  following directory structure:
9 *
10 *  XXXX fill this in.
11 *    /
12 *    /dev
13 *    /dev/XXX   [where XXX includes at least console]
14 */
15
16/*
17 *  COPYRIGHT (c) 1989-2012.
18 *  On-Line Applications Research Corporation (OAR).
19 *
20 *  The license and distribution terms for this file may be
21 *  found in the file LICENSE in this distribution or at
22 *  http://www.rtems.org/license/LICENSE.
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include <stdio.h>
30
31#include <pmacros.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <errno.h>
37#include <string.h>
38#include <inttypes.h>
39#include <ctype.h>
40#include <reent.h>
41#include <rtems/imfs.h>
42
43#include <rtems.h>
44#include <rtems/libio.h>
45
46#include "primode.h"
47
48const char rtems_test_name[] = "PSXFILE 1";
49
50/* forward declarations to avoid warnings */
51void test_case_reopen_append(void);
52void dump_statbuf(struct stat *buf);
53void stat_a_file(const char *file);
54int test_main(void);
55
56char test_write_buffer[ 1024 ];
57rtems_filesystem_operations_table  IMFS_ops_no_evalformake;
58rtems_filesystem_operations_table  IMFS_ops_no_rename;
59
60/*
61 *  File test support routines.
62 */
63
64void test_cat(
65  char *file,
66  int   offset_arg,
67  int   length
68);
69
70void test_write(
71  char   *file,
72  off_t  offset,
73  char  *buffer
74);
75
76void test_extend(
77  char *file,
78  off_t new_len
79);
80
81/*
82 *  dump_statbuf
83 */
84void dump_statbuf( struct stat *buf )
85{
86  rtems_device_major_number major1;
87  rtems_device_minor_number minor1;
88  rtems_device_major_number major2;
89  rtems_device_minor_number minor2;
90
91  rtems_filesystem_split_dev_t( buf->st_dev, major1, minor1 );
92  rtems_filesystem_split_dev_t( buf->st_rdev, major2, minor2 );
93
94  printf( "....st_dev     (0x%" PRIx32 ":0x%" PRIx32 ")\n", major1, minor1 );
95  printf( "....st_rdev    (0x%" PRIx32 ":0x%" PRIx32 ")\n", major2, minor2 );
96  printf( "....st_ino     %" PRIxino_t "  may vary by small amount\n", buf->st_ino );
97  printf( "....mode  = %08" PRIomode_t "\n", buf->st_mode );
98  printf( "....nlink = %d\n", buf->st_nlink );
99
100  printf( "....uid = %d\n", buf->st_uid );
101  printf( "....gid = %d\n", buf->st_gid );
102
103  printf( "....atime = %s", ctime(&buf->st_atime) );
104  printf( "....mtime = %s", ctime(&buf->st_mtime) );
105  printf( "....ctime = %s", ctime(&buf->st_ctime) );
106
107  printf( "....st_blksize %" PRIxblksize_t "\n", buf->st_blksize );
108  printf( "....st_blocks  %" PRIxblkcnt_t "\n", buf->st_blocks );
109}
110
111void stat_a_file(
112  const char *file
113)
114{
115  int         status;
116  struct stat statbuf;
117
118  rtems_test_assert( file );
119
120  printf( "stat( %s ) returned ", file );
121
122  status = stat( file, &statbuf );
123
124  if ( status == -1 ) {
125    printf( ": %s\n", strerror( errno ) );
126  } else {
127    puts("");
128    dump_statbuf( &statbuf );
129  }
130
131}
132
133static void test_open_directory(void)
134{
135  static const char file[] = "somefile";
136  int status;
137  int fd;
138
139  fd = open( file, O_CREAT, S_IRWXU );
140  rtems_test_assert( fd >= 0 );
141
142  status = close( fd );
143  rtems_test_assert( status == 0 );
144
145#ifdef O_DIRECTORY
146  errno = 0;
147  fd = open( file, O_DIRECTORY, S_IRWXU );
148  rtems_test_assert( fd == -1 );
149  rtems_test_assert( errno == ENOTDIR );
150#endif
151
152  status = unlink( file );
153  rtems_test_assert( status == 0 );
154}
155
156/*
157 *  Main entry point of the test
158 */
159
160#if defined(__rtems__)
161int test_main(void)
162#else
163int main(
164  int argc,
165  char **argv
166)
167#endif
168{
169  int               status;
170  size_t            max_size;
171  int               fd;
172  int               i;
173  struct stat       buf;
174  char              buffer[128];
175  FILE             *file;
176  time_t            atime1;
177  time_t            mtime1;
178  time_t            ctime1;
179  time_t            atime2;
180  time_t            mtime2;
181  time_t            ctime2;
182  rtems_status_code rtems_status;
183  rtems_time_of_day time;
184
185  TEST_BEGIN();
186
187  test_open_directory();
188
189  /*
190   *  Grab the maximum size of an in-memory file.
191   */
192
193  max_size = IMFS_MEMFILE_MAXIMUM_SIZE;
194
195  build_time( &time, 12, 31, 1988, 9, 0, 0, 0 );
196  rtems_status = rtems_clock_set( &time );
197  directive_failed( rtems_status, "clock set" );
198
199  /*
200   *  Simple stat() of /dev/console.
201   */
202
203  puts( "stat of /dev/console" );
204  status = stat( "/dev/console", &buf );
205  rtems_test_assert( !status );
206
207  dump_statbuf( &buf );
208
209  /*
210   *  Exercise mkdir() and some path evaluation.
211   */
212
213  puts( "" );
214  puts( "mkdir /dev/tty" );
215  status = mkdir( "/dev/tty", S_IRWXU );
216  rtems_test_assert( !status );
217
218  puts( "" );
219  puts( "mkdir /usr" );
220  status = mkdir( "/usr", S_IRWXU );
221  rtems_test_assert( !status );
222  puts( "mkdir /etc" );
223  status = mkdir( "/etc", S_IRWXU );
224  rtems_test_assert( !status );
225
226  puts( "mkdir /tmp" );
227  status = mkdir( "/tmp", S_IRWXU );
228  rtems_test_assert( !status );
229
230  /* this tests the ".." path in path name evaluation */
231  puts( "mkdir /tmp/.." );
232  status = mkdir( "/tmp/..", S_IRWXU );
233  rtems_test_assert( status == -1 );
234  rtems_test_assert( errno == EEXIST );
235
236  /* now check out trailing separators */
237  puts( "mkdir /tmp/" );
238  status = mkdir( "/tmp/", S_IRWXU );
239  rtems_test_assert( status == -1 );
240  rtems_test_assert( errno == EEXIST );
241
242  /* try to make a directory under a non-existent subdirectory */
243  puts( "mkdir /j/j1" );
244  status = mkdir( "/j/j1", S_IRWXU );
245  rtems_test_assert( status == -1 );
246  rtems_test_assert( errno == ENOENT );
247
248  /* this tests the ability to make a directory in the current one */
249  puts( "mkdir tmp" );
250  status = mkdir( "tmp", S_IRWXU );
251  rtems_test_assert( status == -1 );
252  rtems_test_assert( errno == EEXIST );
253
254  /*
255   *  Now switch gears and exercise rmdir().
256   */
257
258  puts( "" );
259  puts( "rmdir /usr" );
260  status = rmdir( "/usr" );
261  rtems_test_assert( !status );
262
263  puts( "rmdir /dev" );
264  status = rmdir( "/dev" );
265  rtems_test_assert( status == -1 );
266  rtems_test_assert( errno ==  ENOTEMPTY);
267
268  puts( "rmdir /fred" );
269  status = rmdir ("/fred");
270  rtems_test_assert (status == -1);
271  rtems_test_assert( errno == ENOENT );
272
273  puts( "rmdir /tmp/bha" );
274  status = rmdir( "/tmp/bha" );
275  rtems_test_assert( status == -1 );
276  rtems_test_assert( errno == ENOENT );
277
278  puts( "mknod /dev/test_console" );
279  status = mknod( "/dev/test_console", S_IFCHR, 0LL );
280  rtems_test_assert( !status );
281
282  puts( "mknod /dev/tty/S3" );
283  status = mknod( "/dev/tty/S3", S_IFCHR, 0xFF00000080LL );
284  rtems_test_assert( !status );
285
286  puts ("mknod /etc/passwd");
287  status = mknod( "/etc/passwd", (S_IFREG | S_IRWXU), 0LL );
288  rtems_test_assert( !status );
289
290  puts( "mkdir /tmp/my_dir");
291  status = mkdir( "/tmp/my_dir", S_IRWXU );
292  rtems_test_assert( status == 0 );
293
294  puts("mkfifo /c/my_dir" );
295  status = mkfifo( "/c/my_dir", S_IRWXU );
296  rtems_test_assert( status == -1 );
297
298  /*
299   *  Try to make a directory under a file -- ERROR
300   */
301
302  puts( "mkdir /etc/passwd/j" );
303  status = mkdir( "/etc/passwd/j", S_IRWXU );
304  rtems_test_assert( status == -1 );
305  rtems_test_assert( errno == ENOTDIR );
306
307  /*
308   *  Simple open failure case on non-existent file
309   */
310
311  puts( "open /tmp/joel - should fail with ENOENT" );
312  fd = open( "/tmp/joel", O_RDONLY );
313  rtems_test_assert( fd == -1 );
314  rtems_test_assert( errno == ENOENT );
315
316  /*
317   *  Simple open case where the file is created.
318   */
319
320  puts( "open /tmp/j" );
321  fd = open( "/tmp/j", O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO );
322  rtems_test_assert( fd != -1 );
323  printf( "open returned file descriptor %d\n", fd );
324
325  puts( "close /tmp/j" );
326  status = close( fd );
327  rtems_test_assert( !status );
328
329  puts( "close /tmp/j again" );
330  status = close( fd );
331  rtems_test_assert( status == -1 );
332
333  puts( "unlink /tmp/j" );
334  status = unlink( "/tmp/j" );
335  rtems_test_assert( !status );
336
337  puts( "unlink /tmp" );
338  status = unlink( "/tmp" );
339  rtems_test_assert( status );
340
341  /*
342   *  Simple open failure. Trying to create an existing file.
343   */
344
345  puts("create and close /tmp/tom");
346  fd = open( "/tmp/tom", O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO );
347  rtems_test_assert( fd != -1 );
348  status = close( fd );
349  rtems_test_assert( status == 0 );
350
351  puts("Attempt to recreate /tmp/tom");
352  fd = open( "/tmp/tom", O_CREAT | O_EXCL, S_IRWXU|S_IRWXG|S_IRWXO );
353  rtems_test_assert( fd == -1 );
354  rtems_test_assert( errno == EEXIST );
355
356  puts("create /tmp/john");
357  fd = open( "/tmp/john", O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO );
358  rtems_test_assert( fd != -1 );
359  status = close( fd );
360  rtems_test_assert( status == 0 );
361
362  /*
363   * Open a file in read-only mode and try to truncate
364   */
365
366  puts( "Attempt to create a file, open in read-only mode and truncate it" );
367  fd = open( "/tmp/bha", O_CREAT | O_RDONLY | O_TRUNC, S_IRUSR );
368  rtems_test_assert( fd == -1 );
369  rtems_test_assert( errno == EINVAL );
370
371  puts( "Exercise the reentrant version _link_r -- Expect ENOENT" );
372  status = _link_r( NULL, "/tmp/notexist", "/tmp/cannotexist" );
373  rtems_test_assert( status == -1 );
374  rtems_test_assert( errno == ENOENT );
375
376  puts( "Unlink /tmp/bha using the reentrant version -- OK" );
377  status = _unlink_r( NULL, "/tmp/bha" );
378  rtems_test_assert( status == 0 );
379
380  /*
381   * Simple test case for mknod
382   */
383
384  puts( "mknod with bad type - expect EINVAL" );
385  status = mknod( "/tmp/bha", 0, 0LL );
386  rtems_test_assert( status == -1 );
387  rtems_test_assert( errno == EINVAL );
388
389  /*
390   * Read from filedes opened for write
391   */
392
393  puts( "open /tmp/bha in write only mode -- OK" );
394  fd = open( "/tmp/bha", O_CREAT | O_WRONLY, S_IRWXU|S_IRWXG|S_IRWXO );
395  rtems_test_assert( fd != -1 );
396
397  puts( "attempt fcntl on opened file -- OK" );
398  status = fcntl( fd, F_SETFD, 0 );
399  rtems_test_assert( status == 0 );
400
401  puts( "attempt to read from /tmp/bha - expect EBADF" );
402  status = read( fd, buffer, 10 );
403  rtems_test_assert( status == -1 );
404  rtems_test_assert( errno == EBADF );
405
406  puts( "closing and unlinking /tmp/bha" );
407  status = close( fd );
408  status |= unlink( "/tmp/bha" );
409  rtems_test_assert( status == 0 );
410
411  puts( "open /tmp/bha in read only mode -- OK" );
412  fd = open( "/tmp/bha", O_CREAT | O_RDONLY, S_IRWXU|S_IRWXG|S_IRWXO );
413  rtems_test_assert( fd != -1 );
414
415  puts( "attempt to read from /tmp/bha - expect EBADF" );
416  status = write( fd, buffer, 10 );
417  rtems_test_assert( status == -1 );
418  rtems_test_assert( errno == EBADF );
419
420  puts( "closing and unlinking /tmp/bha" );
421  status = close( fd );
422  status |= unlink( "/tmp/bha" );
423  rtems_test_assert( status == 0 );
424
425  /*
426   * Read/write from an unopened filedes
427   */
428  puts( "attempt to read from an unopened filedes - expect EBADF" );
429  status = read( 5, buffer, 10 );
430  rtems_test_assert( status == -1 );
431  rtems_test_assert( errno == EBADF );
432
433  puts( "attempt to write to an unopened filedes - expect EBADF" );
434  status = write( 5, buffer, 10 );
435  rtems_test_assert( status == -1 );
436  rtems_test_assert( errno == EBADF );
437
438  /*
439   *  Test simple write to a file at offset 0
440   */
441
442  puts( "mknod /tmp/joel" );
443  status = mknod( "/tmp/joel", (S_IFREG | S_IRWXU), 0LL );
444  test_write( "/tmp/joel", 0, "the first write!!!\n" );
445  test_cat( "/tmp/joel", 0, 0 );
446
447  /* Exercise _rename_r */
448
449  /* Simple rename test */
450  puts( "rename /tmp/joel to /tmp/drjoel");
451  status = _rename_r(NULL,"/tmp/joel","/tmp/drjoel");
452  rtems_test_assert(status == 0);
453
454  /* Simple rename test */
455  puts("rename /tmp/drjoel to /tmp/joel");
456  status = _rename_r(NULL,"/tmp/drjoel","/tmp/joel");
457  rtems_test_assert(status == 0);
458
459  /* Invalid old path */
460  puts("rename /tmp/drjoel to /tmp/joel - Should result in an error \
461since old path is not valid");
462  status = _rename_r(NULL,"/tmp/drjoel","/tmp/joel");
463  rtems_test_assert(status == -1);
464
465  /* Invalid new path */
466  puts("rename /tmp/joel to /tmp/drjoel/joel - Should result in an error \
467since new path is not valid");
468  status = _rename_r(NULL,"/tmp/joel","/tmp/drjoel/joel");
469  rtems_test_assert(status == -1);
470
471  puts("changing dir to /tmp");
472  status = chdir("/tmp/");
473  rtems_test_assert(status == 0);
474
475  puts("rename joel to drjoel");
476  status = _rename_r(NULL,"joel","drjoel");
477  rtems_test_assert(status == 0);
478
479  puts("rename drjoel to joel");
480  status = _rename_r(NULL,"drjoel","joel");
481  rtems_test_assert(status == 0);
482
483  /* Rename across file systems */
484  puts("creating directory /imfs");
485  status = mkdir("/imfs",0777);
486  rtems_test_assert(status == 0);
487  puts("creating directory /imfs/hidden_on_mount");
488  status = mkdir("/imfs/hidden_on_mount",0777);
489  rtems_test_assert(status == 0);
490
491  puts("mounting filesystem with IMFS_ops at /imfs");
492  status = mount("null", "/imfs", "imfs", RTEMS_FILESYSTEM_READ_WRITE, NULL);
493  rtems_test_assert(status == 0);
494  puts("creating directory /imfs/test (on newly mounted filesystem)");
495  status = mkdir("/imfs/test", 0777);
496  rtems_test_assert(status == 0);
497
498  puts("attempt to rename directory joel to /imfs/test/joel - should fail with EXDEV");
499  status = _rename_r(NULL, "joel", "/imfs/test/joel");
500  rtems_test_assert(status == -1);
501  rtems_test_assert(errno == EXDEV);
502
503  puts("changing dir to /");
504  status = chdir("/");
505  rtems_test_assert(status == 0);
506
507  puts("attempt to rename across filesystem, with old path having a parent node");
508  puts("attempt to rename tmp/joel to /imfs/test/joel");
509  status = _rename_r(NULL, "tmp/joel", "/imfs/test/joel");
510  rtems_test_assert(status == -1);
511  rtems_test_assert(errno == EXDEV);
512
513  puts("End of _rename_r tests");
514
515  /*
516   *  Test simple write to a file at a non-0 offset in the first block
517   */
518
519  status = unlink( "/tmp/joel" );
520  rtems_test_assert( !status );
521
522  status = mknod( "/tmp/joel", (S_IFREG | S_IRWXU), 0LL );
523  rtems_test_assert( !status );
524
525  test_write( "/tmp/joel", 10, "the first write!!!\n" );
526  test_cat( "/tmp/joel", 0, 0 );
527  stat_a_file( "/tmp/joel" );
528
529  /*
530   *  Test simple write to a file at a non-0 offset in the second block.  Then
531   *  try to read from various offsets and lengths.
532   */
533
534  puts("unlink /tmp/joel");
535  status = unlink( "/tmp/joel" );
536  rtems_test_assert( !status );
537
538  /* Test a failure path */
539
540  puts( "unlink /tmp/joel" );
541  status = unlink( "/tmp/joel" );
542  rtems_test_assert( status == -1 );
543
544  puts( "mknod /tmp/joel");
545  status = mknod( "/tmp/joel", (S_IFREG | S_IRWXU), 0LL );
546  rtems_test_assert( !status );
547
548  test_write( "/tmp/joel", 514, "the first write!!!\n" );
549  test_write( "/tmp/joel", 1, test_write_buffer );
550  test_write( "/tmp/joel", 63, test_write_buffer );
551  test_cat( "/tmp/joel", 0, 1 );
552  test_cat( "/tmp/joel", 1, 1 );
553  test_cat( "/tmp/joel", 490, 1 );
554  test_cat( "/tmp/joel", 512, 1 );
555  test_cat( "/tmp/joel", 513, 1 );
556  test_cat( "/tmp/joel", 514, 1 );
557  test_cat( "/tmp/joel", 520, 1 );
558  test_cat( "/tmp/joel", 1, 1024 );
559
560  /*
561   *  Read from a much longer file so we can descend into doubly and
562   *  triply indirect blocks.
563   */
564
565  if ( max_size < (size_t) 300 * 1024 ) {
566    test_extend( "/tmp/joel", max_size - 1 );
567    test_cat( "/tmp/joel", max_size / 2, 1024 );
568  } else {
569    printf( "Skipping maximum file size test since max_size is %zu bytes\n", max_size );
570    puts("That is likely to be bigger than the available RAM on many targets." );
571  }
572
573  stat_a_file( "/tmp/joel" );
574
575  /*
576   *  Now try to use a FILE * descriptor
577   *
578   *  /tmp/j should not exist at this point.
579   */
580
581  puts( "stat of /tmp/j" );
582  errno = 0;
583  status = stat( "/tmp/j", &buf );
584  printf( "stat(/tmp/j) returned %d (errno=%d)\n", status, errno );
585  dump_statbuf( &buf );
586
587  puts( "fopen of /tmp/j" );
588  file = fopen( "/tmp/j", "w+" );
589  rtems_test_assert( file );
590
591  puts( "fprintf to /tmp/j" );
592  for (i=1 ; i<=5 ; i++) {
593    status = fprintf( file, "This is call %d to fprintf\n", i );
594    rtems_test_assert( status );
595    printf( "(%d) %d characters written to the file\n", i, status );
596  }
597
598  fflush( file );
599
600  status = stat( "/tmp/j", &buf );
601  rtems_test_assert( !status );
602  dump_statbuf( &buf );
603  atime2 = buf.st_atime;
604  mtime2 = buf.st_mtime;
605  ctime2 = buf.st_ctime;
606
607
608  status = rtems_task_wake_after( rtems_clock_get_ticks_per_second() );
609  rewind( file );
610  while ( fgets(buffer, 128, file) )
611    printf( "%s", buffer );
612
613  /*
614   * Verify only atime changed for a read.
615   */
616  status = stat( "/tmp/j", &buf );
617  rtems_test_assert( !status );
618  dump_statbuf( &buf );
619  atime1 = buf.st_atime;
620  mtime1 = buf.st_mtime;
621  ctime1 = buf.st_ctime;
622  rtems_test_assert( atime1 != atime2);
623  rtems_test_assert( mtime1 == mtime2);
624  rtems_test_assert( ctime1 == ctime2);
625
626  unlink( "/tmp/joel" );
627
628  /*
629   *  Now truncate a file
630   */
631
632  status = rtems_task_wake_after( rtems_clock_get_ticks_per_second() );
633  puts( "truncate /tmp/j to length of 40" );
634  status = truncate( "/tmp/j", 40 );
635  rtems_test_assert( !status );
636
637  /*
638   * Verify truncate changed all except atime.
639   */
640  status = stat( "/tmp/j", &buf );
641  rtems_test_assert( !status );
642  dump_statbuf( &buf );
643  atime2 = buf.st_atime;
644  mtime2 = buf.st_mtime;
645  ctime2 = buf.st_ctime;
646  rtems_test_assert( atime1 == atime2);
647  rtems_test_assert( mtime1 != mtime2);
648  rtems_test_assert( ctime1 != ctime2);
649
650  /* try to truncate the console and see what happens */
651  errno = 0;
652  status = truncate( "/dev/console", 40 );
653  rtems_test_assert( status == 0 || ( status == -1 && errno == EINVAL ) );
654
655  puts( "truncate /tmp/j to length of 0" );
656  status = truncate( "/tmp/j", 0 );
657  rtems_test_assert( !status );
658
659  puts( "truncate /tmp to length of 0 should fail with EISDIR\n");
660  status = truncate( "/tmp", 0 );
661  rtems_test_assert( status == -1 );
662  printf( "%d: %s\n", errno, strerror( errno ) );
663  rtems_test_assert( errno == EISDIR );
664
665  status = truncate( "/tmp/fred", 10 );
666  rtems_test_assert( status == -1);
667
668  rtems_status = rtems_io_register_name( "/dev/not_console", 0, 0 );
669  directive_failed( rtems_status, "io register" );
670
671  test_case_reopen_append();
672
673  TEST_END();
674  rtems_test_exit( 0 );
675}
676
677/*
678 *  Open/Create a File and write to it
679 *
680 *  Test case submitted by Andrew Bythell <abythell@nortelnetworks.com>.
681 *
682 */
683
684void test_file (char *filename, char *mode);
685
686void test_case_reopen_append(void)
687{
688  printf ("Writing First File\n");
689  test_file ("/one.txt", "a");
690  test_file ("/one.txt", "a");
691
692  /* but not the second time - this will insert junk.
693     the number of ^@'s seems to equal the number of
694     actual characters in the file */
695
696  printf ("Writing Second File\n");
697  test_file ("/two.txt", "a");
698  test_file ("/two.txt", "a");
699
700  test_cat( "/one.txt", 0, 1024 );
701  test_cat( "/two.txt", 0, 1024 );
702}
703
704void test_file (char *filename, char *mode)
705{
706  FILE *fp;
707  fp = fopen (filename, mode);
708  if (!fp)
709      perror ("fopen");
710  fprintf (fp, "this is a test line\n");
711  if (fclose (fp))
712      perror ("fclose");
713}
Note: See TracBrowser for help on using the repository browser.