StreamNative.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2009 Rexx Language Association. All rights reserved. */
5 /* */
6 /* This program and the accompanying materials are made available under */
7 /* the terms of the Common Public License v1.0 which accompanies this */
8 /* distribution. A copy is also available at the following address: */
9 /* http://www.ibm.com/developerworks/oss/CPLv1.0.htm */
10 /* */
11 /* Redistribution and use in source and binary forms, with or */
12 /* without modification, are permitted provided that the following */
13 /* conditions are met: */
14 /* */
15 /* Redistributions of source code must retain the above copyright */
16 /* notice, this list of conditions and the following disclaimer. */
17 /* Redistributions in binary form must reproduce the above copyright */
18 /* notice, this list of conditions and the following disclaimer in */
19 /* the documentation and/or other materials provided with the distribution. */
20 /* */
21 /* Neither the name of Rexx Language Association nor the names */
22 /* of its contributors may be used to endorse or promote products */
23 /* derived from this software without specific prior written permission. */
24 /* */
25 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
26 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
27 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
28 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
29 /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
30 /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
31 /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
32 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
33 /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
34 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
35 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 /* */
37 /*----------------------------------------------------------------------------*/
38 /******************************************************************************/
39 /* REXX Kernel */
40 /* */
41 /* Stream processing (stream oriented file systems) */
42 /* */
43 /******************************************************************************/
44 
45 #include "oorexxapi.h"
46 #include "StreamNative.hpp"
47 #include "StreamCommandParser.h"
48 #include "Utilities.hpp"
49 #include <fcntl.h>
50 #include <sys/stat.h>
51 #include <string.h>
52 #include <errno.h>
53 
54 // jlf: I assume I can't include the constants...
55 #define CHAR_positional "positional"
56 
57 /********************************************************************************/
58 /* */
59 /* Data area's for open routines */
60 /* */
61 /********************************************************************************/
62 
63 // name of our stream object structure (automatically retrieved for us)
64 const char *StreamInfoProperty = "CSELF";
65 
66 /*****************************************************************/
67 /* declares needed for command seek/position */
68 /*****************************************************************/
69 
70 const int operation_read = 0x01;
71 const int operation_write = 0x02;
72 const int operation_nocreate = 0x04;
73 const int position_by_char = 0x04;
74 const int position_by_line = 0x08;
75 const int position_offset_specified = 0x10;
76 
77 /*****************************************************************/
78 /* declares needed for command query seek/position */
79 /*****************************************************************/
80 
81 const int query_read_position = 0x01;
82 const int query_write_position = 0x02;
83 const int query_char_position = 0x04;
84 const int query_line_position = 0x08;
85 const int query_system_position = 0x10;
86 
87 
88 // short hand defines for some file states
89 #define RDWR_CREAT (RX_O_RDWR | RX_O_CREAT)
90 #define WR_CREAT (RX_O_WRONLY | RX_O_CREAT)
91 #define IREAD_IWRITE (RX_S_IREAD | RX_S_IWRITE)
92 
93 /**
94  * Helper routine for the method stubs to validate the
95  * stream info pointer to ensure that the stream has not
96  * been uninited already. This can occur when an uninit
97  * method for another object attempts to use a stream.
98  *
99  * @param context The method context
100  * @param streamPtr The CSELF value
101  * @param result A potential result object set into the context.
102  *
103  * @return The StreamInfo object instance. Throws an exception if
104  * this is not valid.
105  */
106 StreamInfo *checkStreamInfo(RexxMethodContext *context, void *streamPtr, RexxObjectPtr result)
107 {
108  if (streamPtr == NULL)
109  {
110  context->RaiseException0(Rexx_Error_System_service);
112  }
113 
114  StreamInfo *stream_info = (StreamInfo *)streamPtr;
115  stream_info->setContext(context, result);
116  return stream_info;
117 }
118 
119 
120 /**
121  * Token parsing routine for record length parsing.
122  *
123  * @param ttsp The token action definition associated with this parse.
124  * @param token The current token.
125  * @param userparms An opaque user parameter info.
126  *
127  * @return 0 for success, 1 for any failure.
128  */
129 int reclength_token(TokenDefinition* ttsp, StreamToken &tokenizer, void *userparms)
130 {
131  /* get the next token in TokenString */
132  if (tokenizer.nextToken())
133  {
134  int offset = 0;
135 
136  // must be convertable
137  if (!tokenizer.toNumber(offset))
138  {
139  return 1; // non numeric token, error
140  }
141 
142  *((size_t *)userparms) = offset;
143  return 0;
144  }
145 
146  // we default the record length, so push this back into the stream
147  tokenizer.previousToken();
148  return 0;
149 }
150 
151 /**
152  * Token parsing routine for position offsets.
153  *
154  * @param ttsp The token action definition associated with this parse.
155  * @param token The current token.
156  * @param userparms An opaque user parameter info.
157  *
158  * @return 0 for success, 1 for any failure.
159  */
160 int position_offset(TokenDefinition* ttsp, StreamToken &tokenizer, void *userparms)
161 {
162  /* get the next token in TokenString */
163  if (tokenizer.nextToken())
164  {
165  int64_t offset = 0;
166 
167  // must be convertable
168  if (!tokenizer.toNumber(offset))
169  {
170  return 1; // non numeric token, error
171  }
172 
173  *((int64_t *)userparms) = offset;
174  return 0;
175  }
176  /* no next token - position will */
177  /* raise syntax */
178  return 1;
179 }
180 
181 /**
182  * Token parsing routine for unknown tokens. This just forces a parsing error.
183  *
184  * @param ttsp The token action definition associated with this parse.
185  * @param token The current token.
186  * @param userparms An opaque user parameter info.
187  *
188  * @return 0 for success, 1 for any failure.
189  */
190 int unknown_tr(TokenDefinition* ttsp, StreamToken &tokenizer, void *userparms)
191 {
192  return 1;
193 }
194 
195 /**
196  * Get the buffer attached to this stream. If the existing
197  * buffer is large enough for the requested usage, then use
198  * it. Otherwise, allocate a larger one.
199  *
200  * @param length Required buffer length
201  *
202  * @return Pointer to the buffer area.
203  */
204 char *StreamInfo::allocateBuffer(size_t length)
205 {
206  // if we already have a sufficiently large buffer, just use it.
207  if (bufferAddress != NULL)
208  {
209  if (bufferLength >= length)
210  {
211  return bufferAddress; /* return the existing one */
212  }
213  bufferAddress = (char *)realloc(bufferAddress, length);
214  }
215  else
216  {
217  // make sure we get at least the minimum
218  if (length < DefaultBufferSize)
219  {
220  length = DefaultBufferSize;
221  }
222 
223  bufferAddress = (char *)malloc(length);
224  }
225  bufferLength = length;
226  if (bufferAddress == NULL)
227  {
229  }
230  // and return the new buffer address
231  return bufferAddress;
232 }
233 
234 /**
235  * Get a buffer of at least the default size, returning a
236  * to the buffer and the current size.
237  *
238  * @param length The returned buffer length.
239  *
240  * @return The pointer to the allocated buffer.
241  */
242 char *StreamInfo::getDefaultBuffer(size_t &length)
243 {
244  // make sure we have at least a minimum size buffer allocated,
245  // then return the pointer and length
247  length = bufferLength;
248  return bufferAddress;
249 }
250 
251 
252 /**
253  * Extend the current I/O buffer, keeping any data in the
254  * buffer intact.
255  *
256  * @param length The length of the extended buffer.
257  *
258  * @return Pointer to the reallocated buffer.
259  */
260 char *StreamInfo::extendBuffer(size_t &length)
261 {
262  // We need more room for reading...extend this by another allocation unit,
263  // keeping the data in the new buffer.
265  length = bufferLength;
266  return bufferAddress;
267 }
268 
269 
270 /**
271  * Release the buffer, if any, attached to this stream object.
272  */
274 {
275  // Free file buffer so it can be collected next time garbage collection
276  // is invoked.
277  if (bufferAddress != NULL)
278  {
279  free(bufferAddress);
280  bufferAddress = NULL;
281  bufferLength = 0;
282  }
283 }
284 
285 /**
286  * Open the stream in the specified mode.
287  *
288  * @param openFlags Open flags as defined by the _sopen() library function
289  * @param openMode Mode flags as defined by the _sopen() function.
290  * @param sharedFlag Sharing flags as defined by _sopen()
291  *
292  * @return true if the file is opened successfully, false for any failures.
293  */
294 bool StreamInfo::open(int openFlags, int openMode, int sharedFlag)
295 {
296  return fileInfo.open(qualified_name, openFlags, openMode, sharedFlag);
297 }
298 
299 /**
300  * Retrieve the size of the stream, if available.
301  *
302  * @return The 64-bit stream size of the target stream.
303  */
305 {
306  int64_t streamSize;
307  fileInfo.getSize(streamSize);
308  return streamSize;
309 }
310 
311 /**
312  * Raise a stream error condition, including raising a NOTREADY
313  * condition.
314  */
316 {
318 }
319 
320 /**
321  * Raise a stream error condition, including raising a NOTREADY
322  * condition.
323  *
324  * @param error_code The Rexx error condition to raise.
325  * @param result A NOTREADY condition result object.
326  */
327 void StreamInfo::notreadyError(int error_code, RexxObjectPtr result)
328 {
329  // if we don't have a result specified, use the default one.
330  if (result == NULLOBJECT)
331  {
332  result = defaultResult;
333  }
334  state = StreamError;
335  errorInfo = error_code;
336  fileInfo.clearErrors(); // clear any errors if the stream is open
337  // raise this as a notready condition
338  // NOTE: The additional information only needs to
339  context->RaiseCondition("NOTREADY", context->String(stream_name), self, result);
340  // throw the stream object as an exception to unwind
341  throw this;
342 }
343 
344 /**
345  * Raise an exception for the stream code.
346  *
347  * @param err The raised error code.
348  */
350 {
351  context->RaiseException0(err);
352  // and throw a C++ exception to go back to base camp.
353  throw err;
354 }
355 
356 /**
357  * Raise an exception for the stream code.
358  *
359  * @param err The raised error code.
360  * @param sub1 First error substitution value.
361  */
363 {
364  context->RaiseException1(err, sub1);
365  // and throw a C++ exception to go back to base camp.
366  throw err;
367 }
368 
369 /**
370  * Raise an exception for the stream code.
371  *
372  * @param err The raised error code.
373  * @param sub1 First error substitution value.
374  * @param sub2 Second error substitution value.
375  */
377 {
378  context->RaiseException2(err, sub1, sub2);
379  // and throw a C++ exception to go back to base camp.
380  throw err;
381 }
382 
383 
384 /**
385  * Raise an exception for the stream code.
386  *
387  * @param err The raised error code.
388  * @param sub1 First error substitution value.
389  * @param sub2 Second error substitution value.
390  * @param sub3 Third error substitution value.
391  */
393 {
394  context->RaiseException3(err, sub1, sub2, sub3);
395  // and throw a C++ exception to go back to base camp.
396  throw err;
397 }
398 
399 
400 /**
401  * Raise an exception for the stream code.
402  *
403  * @param err The raised error code.
404  * @param sub1 First error substitution value.
405  * @param sub2 Second error substitution value.
406  * @param sub3 Third error substitution value.
407  * @param sub4 Fourth error substitution value.
408  */
410 {
411  context->RaiseException4(err, sub1, sub2, sub3, sub4);
412  // and throw a C++ exception to go back to base camp.
413  throw err;
414 }
415 
416 
417 /**
418  * Raise an exception for the stream code.
419  *
420  * @param err The raised error code.
421  * @param sub1 First error substitution value.
422  * @param sub2 Second error substitution value.
423  * @param sub3 Third error substitution value.
424  * @param sub4 Fourth error substitution value.
425  * @param sub5 Fifth error substitution value.
426  */
428 {
429  context->RaiseException5(err, sub1, sub2, sub3, sub4, sub5);
430  // and throw a C++ exception to go back to base camp.
431  throw err;
432 }
433 
434 
435 /**
436  * Process an EOF condition for a stream.
437  */
439 {
441 }
442 
443 
444 /**
445  * Process an EOF condition for a stream.
446  *
447  * @param result A result object returned with the NotReady condition.
448  */
450 {
451  /* place this in an eof state */
452  state = StreamEof;
453  /* raise this as a notready condition*/
454  context->RaiseCondition("NOTREADY", context->String(stream_name), self, result);
455 
456  // if a result object was given, the caller's not expecting control back, so
457  // throw an exception to unwind.
458  throw this;
459 }
460 
461 /**
462  * Raise the appropriate not ready condition, checking first for an eof
463  * condition.
464  *
465  * @param result A result object to be passed with the Notready condition.
466  */
468 {
469  // if this is an eof condition, raise the eof not ready
470  if (fileInfo.atEof())
471  {
472  eof();
473  }
474  else
475  {
476  // must be an error, so raise the error not ready using the file error
477  // information
478  notreadyError();
479  }
480 }
481 
482 
483 /**
484  * Get the type of the stream in question.
485  *
486  * @param binary Indicates whether we believe this to be a binary stream,
487  * or not.
488  */
490 {
491  // reset the current type flags
492  transient = false;
493  // see if the system believes this is transient.
494  if (!fileInfo.isTransient())
495  {
496  // non-transient, now process this as binary or text based.
497  if (record_based)
498  {
499  // not given as a binary record length?
500  if (!binaryRecordLength)
501  {
502  // one stream, one record, up to the record size restriction
503  binaryRecordLength = (size_t)size();
504  if (binaryRecordLength == 0)
505  {
506  // raise an exception for this
508  }
509  }
510  }
511  }
512  else // a transient stream
513  {
514  transient = true;
515  // for transient binary streams, if a record length was not provided,
516  // we default to 1.
517  if (record_based)
518  {
519  if (binaryRecordLength == 0)
520  {
521  binaryRecordLength = 1;
522  }
523  }
524  }
525 }
526 
527 /**
528  * Close stream, performing any data flushes that might be
529  * required.
530  *
531  * This raises a NOTREADY condition if any errors occur on the
532  * close.
533  */
535 {
536  // do the stream close
537  bool closed = fileInfo.close();
538  // free our data buffer
539  freeBuffer();
540  // and raise a NOTREADY condition if anything went amiss
541  if (!closed)
542  {
543  defaultResult = context->WholeNumberToObject(fileInfo.errorInfo());
544  notreadyError();
545  }
546  // no longer open for business
547  isopen = false;
549 }
550 
551 /**
552  * Helper function to determine if opts contains the no buffer option.
553  *
554  * @param opts String to search in for NOBUFFER.
555  *
556  * @return True if nobuffer, caseless, is in opts, otherwise false.
557  */
558 bool hasNoBufferOption(const char *opts)
559 {
560  char *tmp = (char *)malloc(strlen(opts) + 1);
561  if (tmp == NULL)
562  {
563  return false;
564  }
565 
566  strcpy(tmp, opts);
567  Utilities::strupper(tmp);
568 
569  bool result = strstr(tmp, "NOBUFFER") != NULL ? true : false;
570  free(tmp);
571 
572  return result;
573 }
574 
575 /**
576  * Open a standard stream, using the provided options string.
577  *
578  * @param options Open parameters, in character string form.
579  *
580  * @return The open condition string.
581  */
582 const char *StreamInfo::openStd(const char *options)
583 {
584  // first check for the standard io streams
587  {
588  // indicate this is stdin
589  fileInfo.setStdIn();
590  // this is a read only file
591  read_only = 1;
592  }
593 
594  else if (!Utilities::strCaselessCompare(stream_name,"STDOUT") ||
596  {
597  // indicate this is stdout
599  // stdout can only be appended to.
600  append = 1;
601  }
602  else /* must be standard error */
603  {
604  // indicate this is stderr
606  // stderr can only be appended to.
607  append = 1;
608  }
609 
610  // check to see if buffering is allowed.
611  if (options != NULL && hasNoBufferOption(options))
612  {
613  nobuffer = 1;
614  }
615  else
616  {
617  nobuffer = 0; /* buffering is used */
618  }
619 
620  // the resolved name is the same as the input name.
621  strcpy(qualified_name, stream_name);
622  // we're open, and ready
623  isopen = true;
624 
625  state = StreamReady;
626 
627  // and also record the transient nature of this
628  transient = fileInfo.isTransient();
629 
630  // don't buffer if we've explicitly requested no buffering.
631  if (nobuffer)
632  {
633  // we do not buffer buffer file
634  fileInfo.setBuffering(false, 0);
635  }
636  else
637  {
638  // we buffer file
639  fileInfo.setBuffering(true, 0);
640  }
641  // this was successful.
642  return "READY:";
643 }
644 
645 /**
646  * Do a stream open using a supplied file handle. This
647  * open process parses all of the open parameters, setting
648  * the appropriate state
649  *
650  * @param options The character string open optins.
651  *
652  * @return The stream state (READY/NOTREADY)
653  */
654 const char *StreamInfo::handleOpen(const char *options)
655 {
656  int oflag = 0; // no default options
657 
658  // reset the standard fields
659  resetFields();
660 
661  /* copy into the full name from the name */
662  strcpy(qualified_name,stream_name);
663 
664  // do we have options?
665  if (options != NULL)
666  {
667  /* Action table for open parameters */
668  ParseAction OpenActionread[] = {
671  ParseAction(BitOr, oflag, RX_O_RDONLY),
672  ParseAction(SetBool, read_only, true),
673  ParseAction()
674  };
675  ParseAction OpenActionwrite[] = {
678  ParseAction(BitOr, oflag, WR_CREAT),
680  ParseAction()
681  };
682  ParseAction OpenActionboth[] = {
685  ParseAction(BitOr, oflag, RDWR_CREAT),
687  ParseAction()
688  };
689  ParseAction OpenActionnobuffer[] = {
690  ParseAction(SetBool, nobuffer, true),
691  ParseAction()
692  };
693  ParseAction OpenActionbinary[] = {
696  ParseAction()
697  };
698  ParseAction OpenActionreclength[] = {
699  ParseAction(MIB, record_based, true),
701  ParseAction()
702  };
703 
704  /* Token table for open parameters */
705  TokenDefinition tts[] = {
706  TokenDefinition("READ",3, OpenActionread),
707  TokenDefinition("WRITE",1, OpenActionwrite),
708  TokenDefinition("BOTH",2, OpenActionboth),
709  TokenDefinition("NOBUFFER",3, OpenActionnobuffer),
710  TokenDefinition("BINARY",2, OpenActionbinary),
711  TokenDefinition("RECLENGTH",3,OpenActionreclength),
713  };
714  /* call the parser to setup the input information */
715  /* the input string should be upper cased */
716  if (parser(tts, options, NULL) != 0)
717  {
719  }
720  }
721 
722 /********************************************************************************************/
723 /* if it is a persistant stream put the write character pointer at the end */
724 /* need to check if the last character is end of file and if so write over it */
725 /* if the stream was created it will have a size of 0 but this will mess up the logic */
726 /* so set it to one */
727 /********************************************************************************************/
728 
730  {
731  if (size() > 0)
732  {
733  // if the stream is persistent, check the end position to see
734  // if there is an eof marker. If there is, position to overwrite the
735  // eof character.
736  // position at the end, and set the write position
738 
739  char char_buffer = ' ';
740  size_t bytesRead;
741  // read the last character of the buffer.
742  // we don't call readBuffer() for this because it
743  // moves the read pointer
744  if (!fileInfo.read(&char_buffer, 1, bytesRead))
745  {
746  if (!write_only) notreadyError();
747  }
748 
749  // if the last character is not a ctrl_z, we need to
750  // step past it.
751  if (ctrl_z != char_buffer)
752  {
754  /* error on Windows so we had to put in that */
755  /* explicitly set the position */
757  }
758  }
759  lineWritePosition = 0;
761  }
762  // ready to go here
763  isopen = true;
764  state = StreamReady;
765  /* go process the stream type */
766  checkStreamType();
767  return "READY:"; /* return success */
768 }
769 
770 
771 /**
772  * Reinitialize the stream fields to default values.
773  */
775 {
776  // initialize the stream info
777  // in case this is not the first
778  // open for this stream
779  strcpy(qualified_name, "\0");
780  fileInfo.reset();
781  stream_line_size = 0;
782  binaryRecordLength = 0;
783  read_only = false;
784  write_only = false;
785  read_write = false;
786  stdstream = false;
787  append = false;
788  opened_as_handle = false;
789  charReadPosition = 1;
790  charWritePosition = 1;
791  lineReadPosition = 1;
792  lineWritePosition = 1;
795  nobuffer = false;
796  last_op_was_read = true;
797  transient = false;
798  record_based = false;
799  isopen = false;
800  // NB: defaultResult is NOT automatically cleared. Individual stream methods might
801  // set this to a default return value before an implicit open operation takes place,
802  // so clearing this will munge the expected return value.
803 // defaultResult = NULL;
804 }
805 
806 
807 /**
808  * Do an implicit open of the stream...this fully parses
809  * the information to sort out how the open needs to proceed.
810  *
811  * @param type The type of open operation.
812  */
814 {
815  // is this one of the standard streams?
816  // those have their own special open process.
817  if (stdstream)
818  {
819  openStd(NULL);
820  return;
821  }
822  // this could be a directly provided handle
823  else if (opened_as_handle)
824  {
825  handleOpen(NULL);
826  return;
827  }
828 
829  // reset everything to the default.
830  resetFields();
831 
832  // get the fully qualified name
834 
835  // first try for read/write and open file without create if specified
836  // If this is an implicit open, try to open for shared readwrite, otherwise
837  // we'll break the stream BIFs with nested calls.
838  read_write = true;
839  if (type == operation_nocreate)
840  {
842  }
843  else
844  {
846  }
847 
848  // if there was an open error and we have the info to try again - doit
849  if (!fileInfo.isOpen())
850  {
851  // turn off the read/write flag and try opening as write only or read
852  // only, depending on the type specified. Also, be sure and clear
853  // errors, otherwise the reason for this implicit open, such as
854  // linein(), will fail.
855  read_write = false;
857 
858  if (type == operation_write)
859  {
860  // In Windows, all files are readable. Therefore S_IWRITE is
861  // equivalent to S_IREAD | S_IWRITE.
863  write_only = true;
864  }
865  else
866  {
868  read_only = true;
869  }
870 
871  // if there still was an error, raise notready condition
872  if (!fileInfo.isOpen())
873  {
874  // if no result given, format the error return
875  if (defaultResult == NULLOBJECT)
876  {
877  char work[30];
878  snprintf(work, sizeof work, "ERROR:%d", fileInfo.errorInfo());
879  defaultResult = context->NewStringFromAsciiz(work);
880  }
881  notreadyError();
882  return;
883  }
884  }
885  // ready to go...positioning the stream will attempt an implicit open
886  // if this is not on, so flip it now before doing the remainder of the
887  // operations.
888  isopen = true;
889 
890  // persistent writeable stream?
891  if (!fileInfo.isTransient() && !read_only)
892  {
893  // if the stream already exists, so we need to
894  // see if there is a terminationg eof marker.
895  if (size() > 0)
896  {
897  // position at the end, and set the write position
899 
900  char char_buffer = ' ';
901  size_t bytesRead;
902  // read the last character of the buffer.
903  // we don't call readBuffer() for this because it
904  // moves the read pointer
905  if (!fileInfo.read(&char_buffer, 1, bytesRead))
906  {
907  if (!write_only) notreadyError();
908  }
909 
910  // if the last character is not a ctrl_z, we need to
911  // step past it.
912  if (ctrl_z != char_buffer)
913  {
915  /* error on Windows so we had to put in that */
916  /* explicitly set the position */
918  }
919  }
920  // set default line positioning
921  lineWritePosition = 0;
923  }
924  state = StreamReady;
925 
926  // go process the stream type
927  checkStreamType();
928 }
929 
930 /**
931  * Set up the stream for reading.
932  */
934 {
935  // make sure we're open
936  if (!isopen)
937  {
939  }
940 
941  // reset to ready state until something goes bad.
942  state = StreamReady;
943 
944  if (!fileInfo.isTransient())
945  {
946  // get the current stream position
947  int64_t tell_position;
948  fileInfo.getPosition(tell_position);
949  /* at the correct position? */
950  if (tell_position != -1 && (charReadPosition - 1) != tell_position)
951  {
952  /* do a seek to charReadPosition */
954  }
955  }
956 }
957 
958 /**
959  * Set up the stream for a write operation.
960  */
962 {
963  // make sure we are properly opened
964  if (!isopen)
965  {
967  }
968 
969  // opened read only? we can't do this
970  // handle as a not ready condition
971  if (read_only)
972  {
973  notreadyError(EACCES);
974  }
975 
976  /* do the open */
977  /* reset to a ready state */
978  state = StreamReady;
979  /* get the current stream position */
980  int64_t tell_position;
981  fileInfo.getPosition(tell_position);
982  /* at the correct position? */
983  if (tell_position != -1 && (charWritePosition - 1) != tell_position)
984  {
985  // not opened for append?
986  if (!append)
987  {
988  /* set stream back to write position */
990  }
991  }
992 }
993 
994 
995 /**
996  * Read a line from the stream
997  *
998  * @param buffer The data to write to the stream
999  * @param length The length of the data buffer.
1000  * @param update_position
1001  * determines whether the read will also update the write position.
1002  *
1003  * @return A string object representing the line.
1004  */
1005 RexxStringObject StreamInfo::readLine(char *buffer, size_t length, bool update_position)
1006 {
1007  size_t bytesRead;
1008 
1009  if (!fileInfo.read(buffer, length, bytesRead))
1010  {
1011  checkEof();
1012  }
1013 
1014  if (bytesRead == 0) /* work ok? */
1015  {
1016  // must be an eof condition
1017  eof();
1018  }
1019  else
1020  {
1021  /* create a result string */
1022  RexxStringObject string = context->NewString(buffer, bytesRead);
1023  if (update_position) /* need to move read position? */
1024  {
1025  /* update the read position */
1026  charReadPosition += bytesRead;
1027  }
1028  if (bytesRead != length) /* not get it all? */
1029  {
1030  defaultResult = string;
1031  eof();
1032  }
1033  return string; /* return the string */
1034  /* go raise a notready condition */
1035  }
1036  return context->NullString(); /* return the string */
1037 }
1038 
1039 
1040 /**
1041  * Convert a specified stream name into it's fully qualified
1042  * name.
1043  */
1045 {
1046  if (strlen(qualified_name) == 0)
1047  {
1049  }
1050 }
1051 
1052 /**
1053  * Write a buffer of data to the stream, raising an notready
1054  * condition if it fails.
1055  *
1056  * @param data Pointer to the first byte of data
1057  * @param length length of the data buffer
1058  * @param bytesWritten
1059  * Actual number of bytes written to the stream.
1060  */
1061 void StreamInfo::writeBuffer(const char *data, size_t length, size_t &bytesWritten)
1062 {
1063  if (!fileInfo.write(data, length, bytesWritten))
1064  {
1065  notreadyError();
1066  }
1067  // we can't update or query the position for a transient stream, so
1068  // we have to assume this was written correctly.
1069  if (!transient)
1070  {
1071  // make sure the current write position is updated after the write.
1073  {
1074  notreadyError();
1075  }
1076  // make sure we keep this origin 1
1078  }
1079 }
1080 
1081 /**
1082  * Write a terminated line of data to the stream, raising an notready
1083  * condition if it fails.
1084  *
1085  * @param data Pointer to the first byte of data
1086  * @param length length of the data buffer
1087  * @param bytesWritten
1088  * Actual number of bytes written to the stream.
1089  */
1090 void StreamInfo::writeLine(const char *data, size_t length, size_t &bytesWritten)
1091 {
1092  if (!fileInfo.putLine(data, length, bytesWritten))
1093  {
1094  notreadyError();
1095  }
1096 
1097  // for non-transient streams, update the output position
1098  if (!transient)
1099  {
1100  // make sure the current write position is updated after the write.
1102  {
1103  notreadyError();
1104  }
1105  // make sure we keep this origin 1
1107  }
1108 }
1109 
1110 /**
1111  * Read a buffer of data from the current position for the
1112  * given length. This also updates our character input
1113  * position information.
1114  *
1115  * @param data The location to place the data.
1116  * @param length The length to read.
1117  * @param bytesRead The number of bytes actually read.
1118  */
1119 void StreamInfo::readBuffer(char *data, size_t length, size_t &bytesRead)
1120 {
1121  if (!fileInfo.read(data, length, bytesRead))
1122  {
1123  notreadyError();
1124  }
1125  // we track the character read position whenever we do a read, so
1126  // update the position for the actual number we've advanced.
1127  charReadPosition += bytesRead;
1128 }
1129 
1130 
1131 /**
1132  * Write out the remainder of an output line for a record oriented I/O operation.
1133  */
1134 void StreamInfo::completeLine(size_t writeLength)
1135 {
1136  // write this out in chunks
1137  char buffer[256];
1138  memset(buffer, ' ', sizeof(buffer)); /* fill buffer with blanks */
1139 
1140  while (writeLength > 0)
1141  {
1142  size_t bytesWritten;
1143  writeBuffer(buffer, writeLength < sizeof(buffer) ? writeLength : sizeof(buffer), bytesWritten);
1144  writeLength -= bytesWritten;
1145  }
1146 }
1147 
1148 /**
1149  * Write out a fixed record line, padding with blanks if the
1150  * line is not of the correct size.
1151  *
1152  * @param data The data to write.
1153  * @param length length of the buffered data.
1154  *
1155  * @return The line residual count.
1156  */
1157 void StreamInfo::writeFixedLine(const char *data, size_t length)
1158 {
1159  /* calculate the length needed */
1160  size_t write_length = binaryRecordLength - (size_t)((charWritePosition % binaryRecordLength) - 1);
1161  // make sure we don't go over the length of the record.
1162  if (length > write_length)
1163  {
1164  length = write_length;
1165  }
1166  // get the padding amount
1167  size_t padding = write_length - length;
1168 
1169  // write the line, then complete with blanks up to the padding length.
1170  writeBuffer(data, length, length);
1171  completeLine(padding);
1172 }
1173 
1174 /**
1175  * Move the stream position, with error checking.
1176  *
1177  * @param position The target position.
1178  * @param newPosition
1179  * The updated final position of the move.
1180  */
1181 void StreamInfo::setPosition(int64_t position, int64_t &newPosition)
1182 {
1183  // if not open yet, open now, but don't create this if doesn't
1184  // already exist.
1185  if (!isopen)
1186  {
1188  }
1189  // Seek to the target position, if possible. The request position
1190  // is a 1-based character number. We need to convert this into
1191  // a zero-based one before moving.
1192  if (!fileInfo.seek(position - 1, SEEK_SET, newPosition))
1193  {
1194  // Failed, raise a not ready condition.
1195  checkEof();
1196  }
1197  // convert the target position back to 1-based.
1198  newPosition++;
1199 }
1200 
1201 /**
1202  * Move the stream position, with error checking.
1203  *
1204  * @param position The target position.
1205  * @param newPosition
1206  * The updated final position of the move.
1207  */
1208 void StreamInfo::setPosition(int64_t offset, int style, int64_t &newPosition)
1209 {
1210  // if doing absolute positioning, then we need to fudge the offset
1211  if (style == SEEK_SET)
1212  {
1213  offset--;
1214  }
1215  // Seek to the target position, if possible. The request position
1216  // is a 1-based character number. We need to convert this into
1217  // a zero-based one before moving.
1218  if (!fileInfo.seek(offset, style, newPosition))
1219  {
1220  // Failed, raise a not ready condition.
1221  checkEof();
1222  }
1223  // convert the target position back to 1-based.
1224  newPosition++;
1225 }
1226 
1227 /**
1228  * Sets the current read position for the stream. This
1229  * updates charReadPosition to point to the target location.
1230  *
1231  * @param position The target character position (Rexx coordinates, which
1232  * means 1 based rather than the native zero-based).
1233  */
1235 {
1236  setPosition(position, charReadPosition);
1237 }
1238 
1239 /**
1240  * Sets the current write position for the stream. This
1241  * updates charWritePosition to point to the target location.
1242  *
1243  * @param position The target character position (Rexx coordinates, which
1244  * means 1 based rather than the native zero-based).
1245  */
1247 {
1248  setPosition(position, charWritePosition);
1249 }
1250 
1251 
1252 /**
1253  * Set the current character read position, raising a NOTREADY
1254  * condition of there is a problem.
1255  *
1256  * @param position The target stream position.
1257  * @param result A result object returned to the caller after raising the
1258  * condition.
1259  */
1261 {
1262  if (transient) /* trying to move a transient stream?*/
1263  {
1265  }
1266 
1267  if (position < 1) /* too small? */
1268  {
1269  raiseException(Rexx_Error_Incorrect_method_positive, context->String(CHAR_positional), context->WholeNumberToObject(1), context->Int64ToObject(position));
1270  }
1271  /* make sure we're within the bounds */
1272  if (size() >= position)
1273  {
1274  // try to move to the new position, raising the appropriate NOTREADY
1275  // if this is a failure.
1276  setReadPosition(position);
1277  }
1278  else
1279  {
1280  // I can't do that Dave...raise an eof NOTREADY.
1281  eof();
1282  }
1283 }
1284 
1285 /**
1286  * Set the line read position.
1287  *
1288  * @param position The target position.
1289  */
1291 {
1292  if (transient) /* trying to move a transient stream?*/
1293  {
1295  }
1296 
1297  if (position < 1) /* too small? */
1298  {
1299  raiseException(Rexx_Error_Incorrect_method_positive, context->String(CHAR_positional), context->WholeNumberToObject(1), context->Int64ToObject(position));
1300  }
1301 
1302  // go set the new locations information.
1304  // and go set our read position appropriately
1306 }
1307 
1308 /**
1309  * Set the char write position.
1310  *
1311  * @param position The target position.
1312  */
1314 {
1315  if (transient) /* trying to move a transient stream?*/
1316  {
1318  }
1319  if (position < 1) /* too small? */
1320  {
1321  raiseException(Rexx_Error_Incorrect_method_positive, context->String(CHAR_positional), context->WholeNumberToObject(1), context->Int64ToObject(position));
1322  }
1323  // go move to this position
1324  setWritePosition(position);
1325 }
1326 
1327 /**
1328  * Set the line write position.
1329  *
1330  * @param position The target position.
1331  * @param result A result value to be used when raising a condition.
1332  */
1334 {
1335  if (transient) /* trying to move a transient stream?*/
1336  {
1337  /* this is an error */
1339 
1340  }
1341  if (position < 1) /* too small? */
1342  {
1343  /* report an error also */
1344  raiseException(Rexx_Error_Incorrect_method_positive, context->String(CHAR_positional), context->WholeNumberToObject(1), context->Int64ToObject(position));
1345 
1346  }
1347 
1348  // go set the new locations information.
1350  // and go set our read position appropriately
1352 }
1353 
1354 
1355 /**
1356  * Read in a variable length line, searching for the eol marker
1357  * for the line.
1358  *
1359  * @return The read line.
1360  */
1362 {
1363  // allocate a buffer for this line. We get a pretty good size one, which will
1364  // most likely be sufficient for most file lines.
1365  size_t bufferSize;
1366  char *buffer = getDefaultBuffer(bufferSize);
1367  size_t currentLength = 0;
1368 
1369  // now loop until get an entire line read in.
1370  for (;;)
1371  {
1372  char *readPosition = buffer + currentLength;
1373  size_t bytesRead = 0;
1374  if (!fileInfo.gets(readPosition, bufferSize - currentLength, bytesRead))
1375  {
1376  checkEof();
1377  }
1378  // update the size of the line now
1379  currentLength += bytesRead;
1380 
1381  // Check for new line character first. If we are at eof and the last
1382  // line ended in a new line, we don't want the \n in the returned
1383  // string.
1384 
1385  // If we have a new line character in the last position, we have
1386  // a line. The gets() function has translated crlf sequences into
1387  // single lf characters.
1388  if (buffer[currentLength - 1] == '\n')
1389  {
1391  return context->NewString(buffer, currentLength - 1);
1392  }
1393 
1394  // No new line but we hit end of file reading this? This will be the
1395  // entire line then.
1396  if (fileInfo.atEof())
1397  {
1399  return context->NewString(buffer, currentLength);
1400  }
1401  buffer = extendBuffer(bufferSize);
1402  }
1403 }
1404 
1405 
1406 /**
1407  * Read in a variable length line, searching for the eol marker
1408  * for the line.
1409  *
1410  * @return The read line.
1411  */
1413 {
1414  // allocate a buffer for this line. We get a pretty good size one, which will
1415  // most likely be sufficient for most file lines.
1416  size_t bufferSize;
1417  char *buffer = getDefaultBuffer(bufferSize);
1418  size_t currentLength = 0;
1419 
1420  // now loop until get an entire line read in.
1421  for (;;)
1422  {
1423  char *readPosition = buffer + currentLength;
1424  size_t bytesRead = 0;
1425  if (!fileInfo.gets(readPosition, bufferSize - currentLength, bytesRead))
1426  {
1427  checkEof();
1428  }
1429 
1430  // update the size of the line now
1431  currentLength += bytesRead;
1432 
1433  // Check for new line character first. If we are at eof and the last
1434  // line ended in a new line, we don't want the \n in the returned
1435  // string.
1436 
1437  // If we have a new line character in the last position, we have
1438  // a line. The gets() function has translated crlf sequences into
1439  // single lf characters.
1440  if (buffer[currentLength - 1] == '\n')
1441  {
1443  context->ArrayAppendString(result, buffer, currentLength - 1);
1444  return;
1445  }
1446 
1447  // No new line but we hit end of file reading this? This will be the
1448  // entire line then.
1449  if (fileInfo.atEof())
1450  {
1452  context->ArrayAppendString(result, buffer, currentLength);
1453  }
1454  buffer = extendBuffer(bufferSize);
1455  }
1456 }
1457 
1458 /**
1459  * Increments the read positions, including the line-orientated positions, after
1460  * a single line has been read. Assumes one line has actually been read.
1461  */
1463 {
1464  // transient streams don't have moveable positions
1465  if (transient)
1466  {
1467  return;
1468  }
1469 
1471  {
1472  notreadyError();
1473  }
1474  // Keep this 1-based.
1475  charReadPosition++;
1476 
1477  lineReadPosition++;
1479  last_op_was_read = true;
1480 }
1481 
1482 /**
1483  * Reset all line-oriented position information after an
1484  * operation that will invalidate the values (for example, a
1485  * charin() or charout() operation).
1486  */
1488 {
1489  // reset all cached line information after an invalidating operation.
1491  lineReadPosition = 0;
1492  stream_line_size = 0;
1493 }
1494 
1495 /**
1496  * Perform a charin() operation on the stream.
1497  *
1498  * @param setPosition
1499  * Indicates whether it is necessary to move the read pointer
1500  * before reading.
1501  * @param position New target position.
1502  * @param read_length
1503  * Length to read.
1504  *
1505  * @return A string object containing the read characters.
1506  */
1507 RexxStringObject StreamInfo::charin(bool _setPosition, int64_t position, size_t read_length)
1508 {
1509  readSetup(); /* do needed setup */
1510  // given a position?...go set it.
1511  if (_setPosition)
1512  {
1513  setCharReadPosition(position);
1514  }
1515  // reading nothing (silly, but allowed)
1516  if (read_length == 0)
1517  {
1518  return context->NullString();
1519  }
1520 
1521  // a buffer string allows us to read the data into an actual string object
1522  // without having to first read it into a separate buffer. Since charin()
1523  // is frequently used to read in entire files at one shot, this can be a
1524  // fairly significant savings.
1525  RexxBufferStringObject result = context->NewBufferString(read_length);
1526  char *buffer = (char *)context->BufferStringData(result);
1527 
1528  // do the actual read
1529  size_t bytesRead;
1530  readBuffer(buffer, read_length, bytesRead);
1531 
1532  // invalidate all of the line positioning info
1534 
1535  // now convert our buffered string into a real string object and return it.
1536  RexxStringObject res = context->FinishBufferString(result, bytesRead);
1537  // if we didn't get the requested amount, return what we got but raise a
1538  // notready condition
1539  if (bytesRead < read_length)
1540  {
1541  eof(res);
1542  }
1543  return res;
1544 }
1545 
1546 /********************************************************************************************/
1547 /* stream_charin */
1548 /********************************************************************************************/
1549 RexxMethod3(RexxStringObject, stream_charin, CSELF, streamPtr, OPTIONAL_int64_t, position, OPTIONAL_size_t, read_length)
1550 {
1551  try
1552  {
1553  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->NullString());
1554  return stream_info->charin(argumentExists(2), position, argumentOmitted(3) ? 1 : read_length);
1555  }
1556  catch (StreamInfo *)
1557  {
1558  }
1559  // this is thrown for any exceptions
1560  catch (int)
1561  {
1562  return 0;
1563  }
1564 
1565  // give default return in case there is an error
1566  return context->NullString();
1567 }
1568 
1569 /**
1570  * Write character data to the stream.
1571  *
1572  * @param data The string object data we're writing.
1573  * @param setPosition
1574  * An order to reset the position information.
1575  * @param position The new write position, if specified.
1576  *
1577  * @return The residual count on the write.
1578  */
1579 size_t StreamInfo::charout(RexxStringObject data, bool _setPosition, int64_t position)
1580 {
1581  // no data given? This is really a close operation.
1582  if (data == NULLOBJECT)
1583  {
1584  // do the setup operations
1585  writeSetup();
1586  // if no position was specified, close this out
1587  if (!_setPosition)
1588  {
1589  close();
1590  }
1591  else
1592  {
1593  setCharWritePosition(position);
1594  }
1595  // no data, no residual!
1596  return 0;
1597  }
1598 
1599  // get the string pointer and length info
1600  size_t length = context->StringLength(data);
1601  const char *stringData = context->StringData(data);
1602  // errors from here return the residual count, so set up the default
1603  // result based on the string size.
1604  defaultResult = context->WholeNumberToObject(length);
1605  // and prepare for the write
1606  writeSetup();
1607  // set the output position to the new location, if given.
1608  if (_setPosition)
1609  {
1610  setCharWritePosition(position);
1611  }
1612  // only write if this is not a null string
1613  if (length > 0)
1614  {
1615  // now write everything out
1616  size_t bytesWritten;
1617  writeBuffer(stringData, length, bytesWritten);
1618  // unable to write for some reason?
1619  if (bytesWritten != length)
1620  {
1621  defaultResult = context->WholeNumberToObject(length - bytesWritten);
1622  notreadyError();
1623  }
1624  }
1625  // reset any line positioning information.
1627  // all written...life is good.
1628  return 0;
1629 }
1630 
1631 
1632 RexxMethod3(size_t, stream_charout, CSELF, streamPtr, OPTIONAL_RexxStringObject, data, OPTIONAL_int64_t, position)
1633 {
1634  try
1635  {
1636  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->False());
1637  return stream_info->charout(data, argumentExists(3), position);
1638  }
1639  // this is thrown for any exceptions
1640  catch (int)
1641  {
1642  return 0;
1643  }
1644  catch (StreamInfo *)
1645  {
1646  }
1647  return 0; // return 0 for all exceptions...the result value has already been set.
1648 }
1649 
1650 /**
1651  * Perform a linein() operation on the stream.
1652  *
1653  * @param setPosition
1654  * Indicates whether it is necessary to move the read pointer
1655  * before reading.
1656  * @param position New target position.
1657  * @param count count of lines to read.
1658  *
1659  * @return A string object containing the read characters.
1660  */
1661 RexxStringObject StreamInfo::linein(bool _setPosition, int64_t position, size_t count)
1662 {
1663  if (count != 1 && count != 0) /* count out of range? */
1664  {
1666  }
1667 
1668  // do read setup
1669  readSetup();
1670  // set a position if we have one
1671  if (_setPosition)
1672  {
1673  /* set the proper position */
1674  setLineReadPosition(position);
1675  }
1676 
1677  if (count == 0) /* nothing to read? */
1678  {
1679  return context->NullString(); /* just return a null string */
1680  }
1681 
1682  // reading fixed length records?
1683  if (record_based)
1684  {
1685  // we need to adjust for any charin operations that might have
1686  // occurred within this record
1687  size_t read_length = binaryRecordLength - (size_t)((charReadPosition % (int64_t)binaryRecordLength) - 1);
1688  // a buffer string allows us to read the data into an actual string object
1689  // without having to first read it into a separate buffer. Since charin()
1690  // is frequently used to read in entire files at one shot, this can be a
1691  // fairly significant savings.
1692  RexxBufferStringObject temp = context->NewBufferString(read_length);
1693  char *buffer = (char *)context->BufferStringData(temp);
1694 
1695  // do the actual read
1696  size_t bytesRead;
1697  readBuffer(buffer, read_length, bytesRead);
1698 
1699  // now convert our buffered string into a real string object and return it.
1700  return context->FinishBufferString(temp, bytesRead);
1701  }
1702  else
1703  {
1704  // we need to read a variable length line
1705  return readVariableLine();
1706  }
1707 }
1708 
1709 
1710 /********************************************************************************************/
1711 /* stream_linein */
1712 /********************************************************************************************/
1713 RexxMethod3(RexxStringObject, stream_linein, CSELF, streamPtr, OPTIONAL_int64_t, position, OPTIONAL_size_t, count)
1714 {
1715  try
1716  {
1717  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->NullString());
1718  return stream_info->linein(argumentExists(2), position, argumentOmitted(3) ? 1 : count);
1719  }
1720  // this is thrown for any exceptions
1721  catch (int)
1722  {
1723  return 0;
1724  }
1725  catch (StreamInfo *)
1726  {
1727  }
1728 
1729  return context->NullString();
1730 }
1731 
1732 /**
1733  * Perform a line-oriented arrayin operation on the stream
1734  *
1735  * @param setPosition
1736  * Indicates whether it is necessary to move the read pointer
1737  * before reading.
1738  * @param position New target position.
1739  * @param count count of lines to read.
1740  *
1741  * @return A string object containing the read characters.
1742  */
1744 {
1745  // do read setup
1746  readSetup();
1747  // reading fixed length records?
1748  if (record_based)
1749  {
1750  while (true)
1751  {
1752  // we need to adjust for any charin operations that might have
1753  // occurred within this record
1754  size_t read_length = binaryRecordLength -
1756  (size_t)(charReadPosition % (int64_t)binaryRecordLength) - 1);
1757  // a buffer string allows us to read the data into an actual string object
1758  // without having to first read it into a separate buffer. Since charin()
1759  // is frequently used to read in entire files at one shot, this can be a
1760  // fairly significant savings.
1761  RexxBufferStringObject temp = context->NewBufferString(read_length);
1762  char *buffer = (char *)context->BufferStringData(temp);
1763 
1764  // do the actual read
1765  size_t bytesRead;
1766  readBuffer(buffer, read_length, bytesRead);
1767 
1768  // now convert our buffered string into a real string object and return it.
1769  context->FinishBufferString(temp, bytesRead);
1770  context->ArrayAppend(result, temp);
1771  }
1772  }
1773  else
1774  {
1775  while (true)
1776  {
1777  // we need to read a variable length line
1778  appendVariableLine(result);
1779  }
1780  }
1781  return 0;
1782 }
1783 
1784 
1785 /********************************************************************************************/
1786 /* native method for doing an arrayin line operation */
1787 /********************************************************************************************/
1788 RexxMethod2(int, stream_arrayin, CSELF, streamPtr, RexxArrayObject, result)
1789 {
1790  try
1791  {
1792  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->NullString());
1793  return stream_info->arrayin(result);
1794  }
1795  // this is thrown for any exceptions
1796  catch (int)
1797  {
1798  }
1799  catch (StreamInfo *)
1800  {
1801  }
1802 
1803  // this will generally terminate because of a NOTREADY condition. We've been filling
1804  // the array in as we go along, so the caller has the reference already, and will
1805  // return whatever we've managed to fill in before the notready occurred;
1806  return 0;
1807 }
1808 
1809 /**
1810  * Count the number lines in the stream.
1811  *
1812  * @param quick Controls whether we just return a 1/0 indicicator that
1813  * there is more data, or do an actualy count of the lines.
1814  *
1815  * @return Either a 1/0 indicator of more or the actual count of lines.
1816  */
1818 {
1819  // if not open yet, open now, but don't create this if doesn't
1820  // already exist.
1821  if (!isopen)
1822  {
1824  }
1825 
1826  // is this a non-persisent stream?
1827  if (fileInfo.isTransient())
1828  {
1829  // just return a success/failure indicator
1830  return fileInfo.hasData() ? 1 : 0;
1831  }
1832  // non-input stream? Never have lines for those
1833  if (!read_only && !read_write)
1834  {
1835  return 0;
1836  }
1837 
1838  // if opened with fixed length records, we just check against the character position.
1839  if (record_based) /* opened as a binary stream? */
1840  {
1841  // get the current size
1842  int64_t currentSize = size();
1843  // already read past that point?
1844  if (charReadPosition > currentSize)
1845  {
1846  return 0;
1847  }
1848 
1849  // now calculate the number of lines in the stream from the size,
1850  // making sure we count any partial lines hanging off the end.
1851  int64_t lineCount = currentSize / binaryRecordLength;
1852  if ((currentSize % binaryRecordLength) > 0)
1853  {
1854  lineCount++;
1855  }
1856 
1857  // get the current line position. We don't need to fudge this...since
1858  // this still gives us the line we're currently within.
1859  int64_t currentLine = (charReadPosition - 1) / binaryRecordLength;
1860 
1861  // and return the delta count.
1862  return lineCount - currentLine;
1863  }
1864  // non-binary persistent stream...these are a pain
1865  else
1866  {
1867  int64_t currentSize = size();
1868 
1869  // if our read position is in no-man's land, this is zero
1870  if (charReadPosition > currentSize)
1871  {
1872  return 0;
1873  }
1874  // if we're doing a quick check, we can return 1 now.
1875  else if (quick)
1876  {
1877  return 1;
1878  }
1879 
1880  // do we have good line size information?
1881  // This is pretty easy to calculate now.
1882  if (stream_line_size > 0 && lineReadPosition > 0)
1883  {
1884  return(stream_line_size - lineReadPosition) + 1;
1885  }
1886 
1887  // need to do an actual scan (bummer)
1888  readSetup();
1889 
1890  // Now go count. If our position is at the beginning,
1891  // this reads everything. Else, it counts from our current
1892  // know line read point and returns the full count based on
1893  // that.
1895  }
1896 }
1897 
1898 /********************************************************************************************/
1899 /* stream_lines */
1900 /********************************************************************************************/
1901 RexxMethod2(int64_t, stream_lines, CSELF, streamPtr, OPTIONAL_CSTRING, option)
1902 {
1903  bool quick = false;
1904  if (option != NULL)
1905  {
1906  if (toupper(*option) == 'N')
1907  {
1908  quick = true;
1909  }
1910  else if (toupper(*option) != 'C')
1911  {
1912  context->RaiseException0(Rexx_Error_Incorrect_method);
1913  return 0;
1914  }
1915  }
1916 
1917  try
1918  {
1919  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->False());
1920  return stream_info->lines(quick);
1921  }
1922  // this is thrown for any exceptions
1923  catch (int)
1924  {
1925  return 0;
1926  }
1927  catch (StreamInfo *)
1928  {
1929  }
1930  return 0;
1931 }
1932 
1933 /**
1934  * Return the remaining character indicator for a stream.
1935  * For transient streams, this is a 1/0 value. For persistent
1936  * streams, this is the remaining data left in the stream.
1937  *
1938  * @return A count of characters in the stream.
1939  */
1941 {
1942  // if not open, we go ahead and open, but do not create implicitly.
1943  if (!isopen)
1944  {
1946  }
1947 
1948  // is this a non-persisent stream?
1949  if (fileInfo.isTransient())
1950  {
1951  // just return a success/failure indicator
1952  return fileInfo.hasData() ? 1 : 0;
1953  }
1954  // non-input stream? Never have lines for those
1955  if (!read_only && !read_write)
1956  {
1957  return 0;
1958  }
1959 
1960  /* check for a negative return value and set it to 0 if neces.*/
1961  int64_t remainder = size() - (charReadPosition - 1);
1962  return remainder > 0 ? remainder : 0;
1963 }
1964 
1965 /********************************************************************************************/
1966 /* stream_chars */
1967 /********************************************************************************************/
1968 RexxMethod1(int64_t, stream_chars, CSELF, streamPtr)
1969 {
1970  try
1971  {
1972  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->False());
1973  return stream_info->chars();
1974  }
1975  // this is thrown for any exceptions
1976  catch (int)
1977  {
1978  return 0;
1979  }
1980  catch (StreamInfo *)
1981  {
1982  }
1983  return 0;
1984 }
1985 
1986 /**
1987  * Write a line out to the stream.
1988  *
1989  * @param data The string object to write.
1990  * @param setPosition
1991  * Indicates whether we've been given a line position to use
1992  * for the write.
1993  * @param position The provided line position.
1994  *
1995  * @return 0 if everything worked. All failures result in notready
1996  * conditions, which throw an exception.
1997  */
1998 int StreamInfo::lineout(RexxStringObject data, bool _setPosition, int64_t position)
1999 {
2000  // nothing to process?
2001  if (data == NULLOBJECT)
2002  {
2003  writeSetup();
2004  // if this is a binary stream, we may have a line to complete
2005  if (record_based)
2006  {
2007  // calculate length to write out
2008  size_t padding = binaryRecordLength - (size_t)((charWritePosition % binaryRecordLength) - 1);
2009  completeLine(padding);
2010  }
2011  // not a line repositioning? we need to close
2012  if (!_setPosition)
2013  {
2014  close();
2015  }
2016  else // setting the line position
2017  {
2018  setLineWritePosition(position);
2019  }
2020  /* set the proper position */
2021  return 0; /* no residual */
2022  }
2023 
2024  // get the specifics
2025  const char *stringData = context->StringData(data);
2026  size_t length = context->StringLength(data);
2027 
2028  writeSetup();
2029  // set the position if needed
2030  if (_setPosition)
2031  {
2032  setLineWritePosition(position);
2033  }
2034 
2035 
2036  // binary mode write?
2037  if (record_based)
2038  {
2039  /* if the line_out is longer than */
2040  /* reclength plus any char out data */
2041  /* raise a syntax error - invalid */
2042  /* call to routine */
2043  if (binaryRecordLength < length + ((charWritePosition % binaryRecordLength) - 1))
2044  {
2046  }
2047 
2048  // write the line out, padding if necessary
2049  writeFixedLine(stringData, length);
2050  // not ready conditions won't return here, so this is successful.
2051  return 0;
2052  }
2053  else
2054  {
2055  // are we keeping count of the lines?
2056  if (stream_line_size > 0)
2057  {
2058  // appending? Then we know we can increase the size
2059  if (append || charWritePosition == size())
2060  {
2061  stream_line_size++;
2062  }
2063  else // the counted number of lines can no longer be relied on.
2064  {
2065  stream_line_size = 0;
2066  }
2067  }
2068  // write the data and line terminator.
2069  writeLine(stringData, length, length);
2070  /* need to adjust line positions? */
2071  if (lineWritePosition > 0)
2072  {
2075  }
2076  return 0; /* line written correctly */
2077  }
2078 }
2079 
2080 /********************************************************************************************/
2081 /* stream_lineout */
2082 /********************************************************************************************/
2083 RexxMethod3(int, stream_lineout, CSELF, streamPtr, OPTIONAL_RexxStringObject, string, OPTIONAL_int64_t, position)
2084 {
2085  try
2086  {
2087  // we give a 1 residual count for all errors
2088  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->True());
2089  return stream_info->lineout(string, argumentExists(3), position);
2090  }
2091  // this is thrown for any exceptions
2092  catch (int)
2093  {
2094  return 0;
2095  }
2096  catch (StreamInfo *)
2097  {
2098  }
2099  return 0; // return 0 for all exceptions...the result value has already been set.
2100 }
2101 
2102 /**
2103  * Close the stream.
2104  *
2105  * @return The character string success/failure indicator.
2106  */
2108 {
2109  // not open, just return a "" value
2110  if (!isopen)
2111  {
2112  state = StreamUnknown;
2113  return ""; /* return empty string */
2114  }
2115 
2116  close(); /* go close the stream */
2117  return "READY:"; /* return the success indicator */
2118 }
2119 
2120 /********************************************************************************************/
2121 /* stream_close */
2122 /********************************************************************************************/
2123 RexxMethod1(CSTRING, stream_close, CSELF, streamPtr)
2124 {
2125  StreamInfo *stream_info = (StreamInfo *)streamPtr;
2126  // we might be getting called from the uninit method after a creation
2127  // error, so only close if we have the stream info
2128  if (stream_info == NULL)
2129  {
2130  return 0;
2131  }
2132 
2133  stream_info->setContext(context, context->NullString());
2134 
2135  try
2136  {
2137  return stream_info->streamClose();
2138  }
2139  // this is thrown for any exceptions
2140  catch (int)
2141  {
2142  return 0;
2143  }
2144  catch (StreamInfo *)
2145  {
2146  }
2147  return 0; // return 0 for all exceptions...the result value has already been set.
2148 }
2149 
2150 
2151 /********************************************************************************************/
2152 /* stream_uninit -- really close this and perform final cleanup */
2153 /********************************************************************************************/
2154 RexxMethod1(CSTRING, stream_uninit, CSELF, streamPtr)
2155 {
2156  StreamInfo *stream_info = (StreamInfo *)streamPtr;
2157  // we might be getting called from the uninit method after a creation
2158  // error, so only close if we have the stream info
2159  if (stream_info == NULL)
2160  {
2161  return 0;
2162  }
2163 
2164  stream_info->setContext(context, context->NullString());
2165 
2166  try
2167  {
2168  stream_info->streamClose();
2169  // delete the stream information backing this. This can only
2170  // be done by the uninit, not by close, because a stream object
2171  // can be reused. We need to wait until the garbage collector
2172  // reclaims this.
2173  delete stream_info;
2174  stream_info = NULL;
2175  // clear the backing pointer for the stream info
2176  context->DropObjectVariable("CSELF");
2177  return 0;
2178  }
2179  // this is thrown for any exceptions
2180  catch (int)
2181  {
2182  return 0;
2183  }
2184  catch (StreamInfo *)
2185  {
2186  }
2187  return 0; // return 0 for all exceptions...the result value has already been set.
2188 }
2189 
2190 /**
2191  * Try to flush the stream, returning the appropriate error
2192  * state if there is a problem.
2193  *
2194  * @return "READY" if everything works. If this fails, a notready
2195  * condition is raised and an exception is thrown.
2196  */
2198 {
2199  // try to flush
2200  if (!fileInfo.flush())
2201  {
2202  char work[30]; /* error information buffer */
2203  snprintf(work, sizeof work, "ERROR:%d", fileInfo.errorInfo()); /* format the error return */
2204  /* go raise a notready condition */
2205  notreadyError(fileInfo.errorInfo(), context->NewStringFromAsciiz(work));
2206  }
2207  return "READY:"; /* return success indicator */
2208 
2209 }
2210 
2211 /********************************************************************************************/
2212 /* stream_flush */
2213 /********************************************************************************************/
2214 RexxMethod1(CSTRING, stream_flush, CSELF, streamPtr)
2215 {
2216  try
2217  {
2218  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->NullString());
2219  return stream_info->streamFlush();
2220  }
2221  // this is thrown for any exceptions
2222  catch (int)
2223  {
2224  return 0;
2225  }
2226  catch (StreamInfo *)
2227  {
2228  }
2229  return 0; // return 0 for all exceptions...the result value has already been set.
2230 }
2231 
2232 /**
2233  * Process explicit stream open requests, handling all of the
2234  * open option variations.
2235  *
2236  * @param options The specified option strings.
2237  *
2238  * @return The READY or NOTREADY strings.
2239  */
2240 const char *StreamInfo::streamOpen(const char *options)
2241 {
2242  int oflag = 0; // no default open options
2243  int pmode = 0; /* and the protection mode */
2244  int shared = RX_SH_DENYRW; /* def. open is non shared */
2245 
2246  // if already open, make sure we close this
2247  if (isopen)
2248  {
2249  close();
2250  }
2251 
2252  // we sorted out the characteristics of this during the init. Make
2253  // sure we open this the appropriate way.
2254  if (stdstream) /* standard stream? */
2255  {
2256  return openStd(options); /* handle as a standard stream */
2257  }
2258  else if (opened_as_handle)
2259  {
2260  /* do a handle open */
2261  return handleOpen(options);
2262  }
2263 
2264 
2265  // reset the standard fields
2266  resetFields();
2267 
2268  // if we have parameters, parse them
2269  if (options != NULL)
2270  {
2271  /* Action table for open parameters */
2272  ParseAction OpenActionread[] = {
2276  ParseAction(ME, oflag, RX_O_TRUNC),
2277  ParseAction(SetBool, read_only, true),
2278  ParseAction(BitOr, oflag, RX_O_RDONLY),
2279  ParseAction(BitOr, pmode, RX_S_IREAD),
2280  ParseAction()
2281  };
2282 
2283  ParseAction OpenActionwrite[] = {
2286  ParseAction(SetBool, write_only, true),
2287  ParseAction(BitOr, oflag, WR_CREAT),
2288  ParseAction(BitOr, pmode, RX_S_IWRITE),
2289  ParseAction()
2290  };
2291  ParseAction OpenActionboth[] = {
2294  ParseAction(SetBool, read_write, true),
2295  ParseAction(BitOr, oflag, RDWR_CREAT),
2296  ParseAction(BitOr, pmode, IREAD_IWRITE),
2297  ParseAction()
2298  };
2299  ParseAction OpenActionappend[] = {
2301  ParseAction(ME, oflag, RX_O_TRUNC),
2302  ParseAction(SetBool, append, true),
2303  ParseAction(BitOr, oflag, RX_O_APPEND),
2304  ParseAction()
2305  };
2306  ParseAction OpenActionreplace[] = {
2308  ParseAction(ME, oflag, RX_O_APPEND),
2309  ParseAction(BitOr, oflag, RX_O_TRUNC),
2310  ParseAction()
2311  };
2312  ParseAction OpenActionnobuffer[] = {
2313  ParseAction(SetBool, nobuffer, true),
2314  ParseAction()
2315  };
2316  ParseAction OpenActionbinary[] = {
2317  ParseAction(MEB, record_based, true),
2319  ParseAction()
2320  };
2321  ParseAction OpenActionreclength[] = {
2324  ParseAction()
2325  };
2326 
2327  ParseAction OpenActionshared[] = {
2328  ParseAction(SetItem, shared, RX_SH_DENYNO),
2329  ParseAction()
2330  };
2331 
2332  ParseAction OpenActionsharedread[] = {
2333  ParseAction(SetItem, shared, RX_SH_DENYWR),
2334  ParseAction()
2335  };
2336 
2337  ParseAction OpenActionsharedwrite[] = {
2338  ParseAction(SetItem, shared, RX_SH_DENYRD),
2339  ParseAction()
2340  };
2341 
2342  #ifdef STREAM_AUTOSYNC
2343  ParseAction OpenActionautosync[] = {
2344  ParseAction(BitOr, oflag, RX_O_SYNC),
2345  ParseAction()
2346  };
2347  #endif
2348 
2349  #ifdef STREAM_SHAREDOPEN
2350  ParseAction OpenActionshareread[] = {
2351  ParseAction(MI, oflag, RX_O_DELAY),
2352  ParseAction(BitOr, oflag, RX_O_RSHARE),
2353  ParseAction()
2354  };
2355  ParseAction OpenActionnoshare[] = {
2356  ParseAction(MI, oflag, RX_O_DELAY),
2357  ParseAction(BitOr, oflag, RX_O_NSHARE),
2358  ParseAction()
2359  };
2360  ParseAction OpenActiondelay[] = {
2361  ParseAction(MI, oflag, RX_O_RSHARE),
2362  ParseAction(MI, oflag, RX_O_NSHARE),
2363  ParseAction(BitOr, oflag, RX_O_DELAY),
2364  ParseAction()
2365  };
2366  #endif
2367 
2368  /* Token table for open parameters */
2369  TokenDefinition tts[] = {
2370  TokenDefinition("READ",3, OpenActionread),
2371  TokenDefinition("WRITE",1, OpenActionwrite),
2372  TokenDefinition("BOTH",2, OpenActionboth),
2373  TokenDefinition("APPEND",2, OpenActionappend),
2374  TokenDefinition("REPLACE",3, OpenActionreplace),
2375  TokenDefinition("NOBUFFER",3, OpenActionnobuffer),
2376  TokenDefinition("BINARY",2, OpenActionbinary),
2377  TokenDefinition("RECLENGTH",3, OpenActionreclength),
2378  TokenDefinition("SHARED",6, OpenActionshared),
2379  TokenDefinition("SHAREREAD",6, OpenActionsharedread),
2380  TokenDefinition("SHAREWRITE",6,OpenActionsharedwrite),
2381 
2382  #ifdef STREAM_AUTOSYNC
2383  TokenDefinition("AUTOSYNC",2, OpenActionautosync),
2384  #endif
2385 
2386  #ifdef STREAM_SHAREDOPEN
2387  TokenDefinition("SHAREREAD",1,OpenActionshareread),
2388  TokenDefinition("NOSHARE",3, OpenActionnoshare),
2389  TokenDefinition("DELAY",1, OpenActiondelay),
2390  #endif
2392  };
2393 
2394  if (parser(tts, options, NULL) != 0)
2395  {
2397  }
2398  }
2399  else
2400  {
2401  // No options, set the defaults.
2402  read_write = true;
2403  append = false;
2404  oflag |= RDWR_CREAT;
2405  pmode |= IREAD_IWRITE;
2406 
2407  // TODO: note that the docs say the default shared mode is SHARED. But,
2408  // the code on entry sets the default to not shared. Need to either fix
2409  // the docs or the code.
2410  }
2411 
2412 
2413  resolveStreamName(); /* get the fully qualified name */
2414 
2415  /* if replace and binary specified, */
2416  /* but not reclength, give back a */
2417  /* syntax error - don't know what to */
2418  /* do */
2419  if (record_based && (oflag & RX_O_TRUNC) && !binaryRecordLength)
2420  {
2422  }
2423 
2424  // If read/write/both/append not specified, the default is BOTH, with the initial
2425  // positioning at the end
2426  // (According to the current doc.)
2427  if (!(oflag & (RX_O_WRONLY | RDWR_CREAT )) && !read_only)
2428  {
2429  oflag |= RX_O_RDWR | RDWR_CREAT; /* set this up for read/write mode */
2430  pmode = IREAD_IWRITE; /* save the pmode info */
2431  read_write = true;
2432  // remember the append status
2433  if ((oflag & RX_O_APPEND) != 0)
2434  {
2435  append = true;
2436  }
2437  }
2438 
2439  if (read_only)
2440  { /* read-only stream? */
2441  /* check if the stream exists */
2443  {
2444  char work[32];
2445 
2446  /* format the error return */
2447  snprintf(work, sizeof work, "ERROR:%d", ENOENT);
2448  /* go raise a notready condition */
2449  notreadyError(ENOENT, context->NewStringFromAsciiz(work));
2450  }
2451  /* and clear all of the write */
2452  /* information */
2453  charWritePosition = 0;
2454  lineWritePosition = 0;
2456  }
2457  /* if write only specified */
2458  /* - try both first */
2459  if (oflag & RX_O_WRONLY)
2460  {
2461  /* set both flags */
2462  read_write = true;
2463  write_only = true;
2464 
2465  oflag &= ~RX_O_WRONLY; /* turn off the write only flag */
2466  oflag |= RDWR_CREAT; /* and turn on the read/write */
2467  pmode = IREAD_IWRITE; /* set the new pmode */
2468  }
2469 
2470  /* now open the stream */
2471  if (!open(oflag, pmode, shared))
2472  {
2473  // if this is some sort of device, it might be output only (i.e., a
2474  // printer). Try opening again write only
2475  // bug 3274050 : no longer limited to device, a regular file can have write-only permissions
2476  if (write_only || fileInfo.isDevice())
2477  {
2478  if (!open(WR_CREAT, RX_S_IWRITE, shared))
2479  {
2480  char work[32];
2481 
2482  snprintf(work, sizeof work, "ERROR:%d", fileInfo.errorInfo()); /* format the error return */
2483  /* go raise a notready condition */
2484  notreadyError(fileInfo.errorInfo(), context->NewStringFromAsciiz(work));
2485  }
2486  read_write = 0;/* turn off the read/write flag */
2487  write_only = 1;/* turn on the write only flag */
2488  }
2489  else
2490  {
2491  char work[32];
2492  snprintf(work, sizeof work, "ERROR:%d", fileInfo.errorInfo()); /* format the error return */
2493  /* go raise a notready condition */
2494  notreadyError(fileInfo.errorInfo(), context->NewStringFromAsciiz(work));
2495  }
2496  }
2497 
2498  // turn off buffering if requested.
2499  if (nobuffer)
2500  {
2501  fileInfo.setBuffering(false, 0);
2502  }
2503  // positioning the stream will test if this is open or not, so mark it open now
2504  isopen = true;
2505 
2506 /********************************************************************************************/
2507 /* if it is a persistant stream put the write character pointer at the end */
2508 /* need to check if the last character is end of file and if so write over it */
2509 /* if the stream was created it will have a size of 0 but this will mess up the logic */
2510 /* so set it to one */
2511 /********************************************************************************************/
2512  /* persistent writeable stream? */
2513  if (!fileInfo.isTransient() && (oflag & (RX_O_WRONLY | RDWR_CREAT)))
2514  {
2515  if (size() > 0)
2516  { /* existing stream? */
2517  // position at the end, and set the write position
2519 
2520  char char_buffer = ' ';
2521  size_t bytesRead;
2522  // read the last character of the buffer.
2523  // we don't call readBuffer() for this because it
2524  // moves the read pointer
2525  if (!fileInfo.read(&char_buffer, 1, bytesRead))
2526  {
2527  if (!write_only) notreadyError();
2528  }
2529 
2530  // if the last character is not a ctrl_z, we need to
2531  // step past it.
2532  if (ctrl_z != char_buffer)
2533  {
2535  /* error on Windows so we had to put in that */
2536  /* explicitly set the position */
2538  }
2539  }
2540  /* set default line positioning */
2541  lineWritePosition = 0;
2543  }
2544  /* this is now ready */
2545  state = StreamReady;
2546  /* go process the stream type */
2547  checkStreamType();
2548  return "READY:"; /* return success */
2549 }
2550 
2551 
2552 /********************************************************************************************/
2553 /* stream_open - open a stream */
2554 /********************************************************************************************/
2555 RexxMethod2(CSTRING, stream_open, CSELF, streamPtr, OPTIONAL_CSTRING, options)
2556 {
2557  try
2558  {
2559  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->NullString());
2560  return stream_info->streamOpen(options);
2561  }
2562  // this is thrown for any exceptions
2563  catch (int)
2564  {
2565  return 0;
2566  }
2567  catch (StreamInfo *)
2568  {
2569  }
2570  return 0; // return 0 for all exceptions...the result value has already been set.
2571 }
2572 
2573 /**
2574  * Set a stream to be opened with a specified handle.
2575  *
2576  * @param fh The input file handle.
2577  */
2579 {
2580  fileInfo.open(fh);
2581  opened_as_handle = 1;
2582 }
2583 
2584 /********************************************************************************************/
2585 /* handle_set */
2586 /* sets the handle into the stream info block */
2587 /********************************************************************************************/
2588 RexxMethod2(int, handle_set, CSELF, streamPtr, int, fh)
2589 {
2590  try
2591  {
2592  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->NullString());
2593  stream_info->setHandle(fh);
2594  }
2595  // this is thrown for any exceptions
2596  catch (int)
2597  {
2598  }
2599  catch (StreamInfo *)
2600  {
2601  }
2602  return 0; // return 0 for all exceptions...the result value has already been set.
2603 }
2604 
2605 /********************************************************************************************/
2606 /* std_set */
2607 /* tags this as a standard I/O stream */
2608 /********************************************************************************************/
2609 RexxMethod1(int, std_set, CSELF, streamPtr)
2610 {
2611  StreamInfo *stream_info = (StreamInfo *)streamPtr;
2612  stream_info->setStandard();
2613  return 0;
2614 }
2615 
2616 /**
2617  * Set the stream position as determined by the options.
2618  *
2619  * @param options The stream command string for positioning.
2620  *
2621  * @return The updated position.
2622  */
2624 {
2625  int style = SEEK_SET; // default style is forward.
2626  bool styleSet = false;
2627  bool seekBack = false;
2628  int position_flags = 0;
2629 
2630  int64_t offset = -1;
2631 
2632  if (options != NULL)
2633  { /* have parameters? */
2634  /* Action table for position parameters */
2635  ParseAction Direction_From_Start[] = {
2636  ParseAction(MEB, styleSet), // anything set is bad
2637  ParseAction(SetItem, style, SEEK_SET),
2638  ParseAction(SetBool, styleSet, true),
2639  ParseAction()
2640  };
2641  ParseAction Direction_From_End[] = {
2642  ParseAction(MEB, styleSet), // anything set is bad
2643  ParseAction(SetItem, style, SEEK_END),
2644  ParseAction(SetBool, styleSet, true),
2645  ParseAction()
2646  };
2647  ParseAction Direction_Forward[] = {
2648  ParseAction(MEB, styleSet), // anything set is bad
2649  ParseAction(SetItem, style, SEEK_CUR),
2650  ParseAction(SetBool, styleSet, true),
2651  ParseAction()
2652  };
2653  ParseAction Direction_Backward[] = {
2654  ParseAction(MEB, styleSet), // anything set is bad
2655  ParseAction(SetItem, style, SEEK_CUR),
2656  ParseAction(SetBool, seekBack, true),
2657  ParseAction(SetBool, styleSet, true),
2658  ParseAction()
2659  };
2660  ParseAction Operation_Read[] = {
2661  ParseAction(ME, position_flags, operation_write),
2662  ParseAction(BitOr, position_flags, operation_read),
2663  ParseAction()
2664  };
2665  ParseAction Operation_Write[] = {
2666  ParseAction(ME, position_flags, operation_read),
2667  ParseAction(BitOr, position_flags, operation_write),
2668  ParseAction()
2669  };
2670  ParseAction Position_By_Char[] = {
2671  ParseAction(ME, position_flags, position_by_line),
2672  ParseAction(BitOr, position_flags, position_by_char),
2673  ParseAction()
2674  };
2675  ParseAction Position_By_Line[] = {
2676  ParseAction(ME, position_flags, position_by_char),
2677  ParseAction(BitOr, position_flags, position_by_line),
2678  ParseAction()
2679  };
2680 
2681  /* Token table for position parameters */
2682  TokenDefinition tts[] = {
2683  TokenDefinition("=",1, Direction_From_Start),
2684  TokenDefinition("<",1, Direction_From_End),
2685  TokenDefinition("+",1, Direction_Forward),
2686  TokenDefinition("-",1, Direction_Backward),
2687  TokenDefinition("READ",1, Operation_Read),
2688  TokenDefinition("WRITE",1, Operation_Write),
2689  TokenDefinition("CHAR",1, Position_By_Char),
2690  TokenDefinition("LINE",1, Position_By_Line),
2692  };
2693 
2694  /* call the parser to fix up */
2695  if (parser(tts, options, (void *)(&offset)) != 0)
2696  {
2698  }
2699  }
2700 
2701  // trying to move a transient stream?
2702  if (transient)
2703  {
2704  /* this is an error */
2706  }
2707 
2708  /* position offset must be specified */
2709  if (offset == -1)
2710  {
2711  raiseException(Rexx_Error_Incorrect_method_noarg, context->String(CHAR_positional), context->NewStringFromAsciiz("SEEK"), context->NewStringFromAsciiz("offset"));
2712  }
2713  // clear any error state...the positioning operation might clear other
2714  // status, such as EOF conditions
2715  state = StreamReady;
2716  /* if read or write was not specified*/
2717  /* check the open flags for read and */
2718  /* set read. check for write and set */
2719  /* write. if open both then set both */
2720  /* flags */
2721  if (!(position_flags & operation_read) && !(position_flags & operation_write))
2722  {
2723  // if this is a read only stream, only one thing we can read
2724  if (read_only)
2725  {
2726  position_flags |= operation_read;
2727  }
2728  /* opened write only? */
2729  else if (write_only)
2730  {
2731  position_flags |= operation_write;
2732  }
2733  else
2734  {
2735  position_flags |= operation_read | operation_write;
2736 
2737  //TODO: make sure the last op was recorded.
2738  /* set both stream pointers to last active position */
2739  if (last_op_was_read)
2740  {
2743  }
2744  else
2745  {
2748  }
2749  }
2750  }
2751 
2752  // if not open yet, open now, but don't create this if doesn't
2753  // already exist.
2754  if (!isopen)
2755  {
2757  }
2758 
2759  /* if the write stream is being */
2760  /* repositioned */
2761  if (position_flags & operation_write)
2762  {
2763  if (append) /* opened append? */
2764  {
2765 
2766  notreadyError(0); // cause a notready condition
2767  return 0;
2768  }
2769  }
2770  /* if moving the read position - */
2771  if (position_flags & operation_read)
2772  {
2773  stream_line_size = 0; /* reset the pseudo lines */
2774  } /* if char or line not specified - */
2775 
2776  /* default to char */
2777  if (!(position_flags & (position_by_char | position_by_line)))
2778  {
2779  position_flags |= position_by_char;
2780  }
2781 
2782  // if this is a backward seek from the current position, we need to negate
2783  // the offset.
2784  if (seekBack)
2785  {
2786  offset = -offset;
2787  }
2788 
2789  // character positioning
2790  if (position_flags & position_by_char)
2791  {
2792  resetLinePositions(); /* reset all line positioning */
2793  // moving the read pointer?
2794  if (position_flags & operation_read)
2795  {
2796  // make sure the file pointer is positioned appropriately.
2797  setPosition(offset, style, charReadPosition);
2798 
2799  // record the one-based position, and if moving both, update the
2800  // write position too.
2801  if (position_flags & operation_write)
2802  {
2804  }
2805  return charReadPosition;
2806  }
2807  else
2808  {
2809  // make sure the file pointer is positioned appropriately.
2810  setPosition(offset, style, charWritePosition);
2811 
2812  // We don't need to handle the
2813  // read position here, since the case above catches both.
2814  return charWritePosition;
2815  }
2816  }
2817  else // line positioning
2818  {
2819  /* if positioning by line and write */
2820  /* only stream, raise notready */
2821  /* because we can't do reads */
2822  if (!(read_write || read_only))
2823  {
2824  return 0;
2825  }
2826 
2827  // moving the read pointer?
2828  if (position_flags & operation_read)
2829  {
2830  // make sure the file pointer is positioned appropriately.
2833 
2834  // update the charReadPosition
2836 
2837  if (position_flags & operation_write)
2838  {
2842  }
2843  return lineReadPosition;
2844  }
2845  else
2846  {
2847  // make sure the file pointer is positioned appropriately.
2850 
2851  return lineWritePosition;
2852  }
2853  }
2854 }
2855 
2856 /**
2857  * Get the line size for this stream. If we're using fixed
2858  * length records, we can calculate this directly from the
2859  * size. If it is a variable-line stream, we might have
2860  * already calculated the size and kept the information. If
2861  * not, we're going to have go and count every line.
2862  *
2863  * @return The number of lines in the stream.
2864  */
2866 {
2867  // using fixed length records?
2868  if (record_based)
2869  {
2870  // this one is fairly simple. Just get the size, and we can calculate
2871  // the end position from that.
2872  int64_t currentSize = size();
2873  int64_t lastLine = currentSize / binaryRecordLength;
2874  // if the current size doesn't have an exact fit, bump the line
2875  // count up one.
2876  if ((currentSize % binaryRecordLength) > 0)
2877  {
2878  lastLine++;
2879  }
2880  return lastLine;
2881  }
2882  else
2883  {
2884  // get a count from the beginning. If we have full information
2885  // already, this will just return that.
2886  return countStreamLines(1, 1);
2887  }
2888 }
2889 
2890 
2891 /**
2892  * Perform a seek operation by line position instead of
2893  * character. The seek can be relative to either the front,
2894  * end, or current positions.
2895  *
2896  * @param offset The offset to move. This can be negative for SEEK_CUR.
2897  * @param direction The position to seek from. This can be SEEK_SET, SEEK_CUR,
2898  * or SEEK_END (using the stdio.h constants directly).
2899  * @param current_line
2900  * The current line position we're seeking from. This can
2901  * be either the stream read or write position. This value
2902  * is updated on completion of the seek.
2903  * @param current_position
2904  * The current character position associated with this
2905  * operation. This is also updated with the seek.
2906  *
2907  * @return The new line position.
2908  */
2909 int64_t StreamInfo::seekLinePosition(int64_t offset, int direction, int64_t &current_line, int64_t &current_position)
2910 {
2911  int64_t newLinePosition = 0;
2912 
2913  switch (direction)
2914  {
2915  case SEEK_END:
2916  {
2917  // end is a little more complicated. We need to find the record #
2918  // of the end position, and go from there.
2919  int64_t lastLine = getLineSize();
2920  newLinePosition = lastLine - offset;
2921  break;
2922  }
2923 
2924  case SEEK_SET:
2925  {
2926  // set is easy, the offset is the new line position. Handle
2927  // everthing from there.
2928  newLinePosition = offset;
2929  break;
2930  }
2931 
2932  case SEEK_CUR:
2933  {
2934  // just add the offset to the current position.
2935  // The offset can be either positive or negative.
2936  newLinePosition = offset + current_line;
2937  break;
2938  }
2939  }
2940 
2941  // we can't seek past the first line.
2942  if (newLinePosition < 1)
2943  {
2944  newLinePosition = 1;
2945  }
2946 
2947  // now go directly and set this.
2948  return setLinePosition(newLinePosition, current_line, current_position);
2949 }
2950 
2951 
2952 /**
2953  * Set a line position to an explicit line number. This takes
2954  * into account differences between record and variable line
2955  * streams.
2956  *
2957  * @param new_line The target new_line position.
2958  * @param current_line
2959  * The current line position we're seeking from.
2960  * @param current_position
2961  * The current character position we're seeking from.
2962  *
2963  * @return The new line position.
2964  */
2965 int64_t StreamInfo::setLinePosition(int64_t new_line, int64_t &current_line, int64_t &current_position)
2966 {
2967  if (new_line <= 1)
2968  {
2969  /* set the position to the beginning */
2970  current_position = 1;
2971  current_line = 1;
2972  return current_line;
2973  }
2974 
2975  if (record_based)
2976  {
2977  // calculate the character position using the record length. +1 is
2978  // added because we're not origin zero for Rexx streams.
2979  current_position = binaryRecordLength * (new_line - 1) + 1;
2980  current_line = new_line;
2981  return current_line;
2982  }
2983  else
2984  {
2985  return seekToVariableLine(new_line, current_line, current_position);
2986  }
2987 }
2988 
2989 
2990 /********************************************************************************************/
2991 /* stream_position */
2992 /********************************************************************************************/
2993 RexxMethod2(int64_t, stream_position, CSELF, streamPtr, CSTRING, options)
2994 {
2995  try
2996  {
2997  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->False());
2998  return stream_info->streamPosition(options);
2999  }
3000  // this is thrown for any exceptions
3001  catch (int)
3002  {
3003  return 0;
3004  }
3005  catch (StreamInfo *)
3006  {
3007  }
3008  return 0; // return 0 for all exceptions...the result value has already been set.
3009 }
3010 
3011 /**
3012  * Determine the count of lines from the file beginning to
3013  * specified stream location.
3014  *
3015  * @param current_position
3016  * The target position we're trying to convert for a char
3017  * position into a line position.
3018  *
3019  * @return A count indicating which logical line current_position lies
3020  * within.
3021  */
3023 {
3024  if (current_position == 0) /* at the beginning? */
3025  {
3026  current_position = 1; /* this is actually the start */
3027  }
3028 
3029  int64_t lastLine;
3030  int64_t count;
3031  // try to count...raise notready if unable to
3032  if (!fileInfo.countLines(0, current_position - 1, lastLine, count))
3033  {
3034  notreadyError();
3035  }
3036  return count; // return the line count
3037 }
3038 
3039 /**
3040  * process a method-level stream query call.
3041  *
3042  * @param options The string options that determine the requested options.
3043  *
3044  * @return The numeric position value as either a line or char position.
3045  */
3047 {
3048  int position_flags = 0; /* clear the parseParms.position_flags */
3049 
3050  // if we have options, parse them into the flag values.
3051  if (options != NULL)
3052  {
3053  /* Action table for query position parameters */
3054 
3055  ParseAction Query_System_Position[] = {
3056  ParseAction(ME, position_flags, query_write_position),
3057  ParseAction(ME, position_flags, query_read_position),
3058  ParseAction(BitOr, position_flags, query_system_position),
3059  ParseAction()
3060  };
3061  ParseAction Query_Read_Position[] = {
3062  ParseAction(ME, position_flags, query_write_position),
3063  ParseAction(ME, position_flags, query_system_position),
3064  ParseAction(BitOr, position_flags, query_read_position),
3065  ParseAction()
3066  };
3067  ParseAction Query_Write_Position[] = {
3068  ParseAction(ME, position_flags, query_read_position),
3069  ParseAction(ME, position_flags, query_system_position),
3070  ParseAction(BitOr, position_flags, query_write_position),
3071  ParseAction()
3072  };
3073  ParseAction Query_Char_Position[] = {
3074  ParseAction(ME, position_flags, query_line_position),
3075  ParseAction(BitOr, position_flags, query_char_position),
3076  ParseAction()
3077  };
3078  ParseAction Query_Line_Position[] = {
3079  ParseAction(ME, position_flags, query_char_position),
3080  ParseAction(BitOr, position_flags, query_line_position),
3081  ParseAction()
3082  };
3083 
3084  /* Token table for open parameters */
3085  TokenDefinition tts[] = {
3086  TokenDefinition("SYS",1, Query_System_Position),
3087  TokenDefinition("READ",1, Query_Read_Position),
3088  TokenDefinition("WRITE",1, Query_Write_Position),
3089  TokenDefinition("CHAR",1, Query_Char_Position),
3090  TokenDefinition("LINE",1, Query_Line_Position),
3092  };
3093  /* parse the command string */
3094  if (parser(tts, options, NULL) != 0)
3095  {
3097  }
3098  }
3099 
3100  // the position of an unopened stream is a null string.
3101  if (!isopen)
3102  {
3103  return context->NullString();
3104  }
3105 
3106  // transient streams alwasy return 1
3107  if (transient)
3108  {
3109  return context->WholeNumberToObject(1); // always a position one
3110  }
3111  // querying the system position?
3112  if (position_flags & query_system_position)
3113  {
3114  int64_t position;
3115  // just get the stream position, raising not ready if it fails.
3116  if (!fileInfo.getPosition(position))
3117  {
3118  notreadyError();
3119  }
3120  return context->Int64ToObject(position);
3121  }
3122  // no method specified?
3123  if (!(position_flags & (query_read_position | query_write_position)))
3124  {
3125  // is this a write-only stream? Return that
3126  if (write_only)
3127  {
3128  position_flags |= query_write_position;
3129  }
3130  else // query the read position by default
3131  {
3132  position_flags |= query_read_position;
3133  }
3134  }
3135  /* asking for the write position? */
3136  if (position_flags & query_write_position)
3137  {
3138  /* check if line or char */
3139  if (position_flags & query_line_position)
3140  {
3141  return context->Int64ToObject(getLineWritePosition());
3142  }
3143  else
3144  {
3145  // just return the character write pointer
3146  return context->Int64ToObject(charWritePosition);
3147  }
3148  }
3149  else // read position
3150  {
3151  if (position_flags & query_line_position)
3152  {
3153  return context->Int64ToObject(getLineReadPosition());
3154  }
3155  else
3156  {
3157  // just return the character write pointer
3158  return context->Int64ToObject(charReadPosition);
3159  }
3160  }
3161 }
3162 
3163 
3164 /**
3165  * Calculate the line position of the stream based on the
3166  * position and the type.
3167  *
3168  * @return The 64-bit line position of the stream.
3169  */
3171 {
3172  // record-based I/O?
3173  if (record_based)
3174  {
3175  // calculate this using the record length
3177  }
3178  else
3179  {
3180  // no up-to-date line position? Recalc this based on the character position.
3181  if (lineReadPosition == 0)
3182  {
3184  }
3185  // set the character position
3187  // return the position.
3188  return lineReadPosition;
3189  }
3190 }
3191 
3192 
3193 
3194 /**
3195  * Calculate the line position of the stream based on the
3196  * defined I/O type.
3197  *
3198  * @return Calculated 64-bit stream line position.
3199  */
3201 {
3202  // using fixed length records?
3203  if (record_based)
3204  {
3205  // the position is based on the size of the records.
3208  }
3209  // standard line-oriented text stream
3210  else
3211  {
3212  // if our line write position has been invalidated, we need to
3213  // recalculate (a real pain!).
3214  if (lineWritePosition == 0)
3215  {
3216  // update the position based on the current character position
3218  }
3219  // synch up the character positioning
3221  // and return the position.
3222  return lineWritePosition;
3223  }
3224 }
3225 
3226 /********************************************************************************************/
3227 /* stream_query_position */
3228 /********************************************************************************************/
3229 RexxMethod2(RexxObjectPtr, stream_query_position, CSELF, streamPtr, OPTIONAL_CSTRING, options)
3230 {
3231  try
3232  {
3233  StreamInfo *stream_info = checkStreamInfo(context, streamPtr, context->NullString());
3234  return stream_info->queryStreamPosition(options);
3235  }
3236  // this is thrown for any exceptions
3237  catch (int)
3238  {
3239  return 0;
3240  }
3241  catch (StreamInfo *)
3242  {
3243  }
3244  return 0; // return 0 for all exceptions...the result value has already been set.
3245 }
3246 
3247 /**
3248  * Move forward the specified number of lines in the file.
3249  *
3250  * @param offset Number of lines to move.
3251  * @param current_line
3252  * Current line position in the file.
3253  * @param current_position
3254  * The current character position to read from.
3255  *
3256  * @return The new current line position.
3257  */
3258 int64_t StreamInfo::readForwardByLine(int64_t offset, int64_t &current_line, int64_t &current_position)
3259 {
3260  readSetup(); /* do additional setup */
3261 
3262  // make sure we're reading from the correct position
3263  setPosition(current_position, current_position);
3264 
3265  // track how many lines are actually moved (move is decremented by seekForwardLines())
3266  int64_t move = offset;
3267 
3268  // remember to do the 1-based / 0-based conversions
3269  if (!fileInfo.seekForwardLines(current_position - 1, move, current_position))
3270  {
3271  // no good, raise an error
3272  notreadyError();
3273  }
3274 
3275  // back to 1-based
3276  current_position++;
3277 
3278  // set this according to the number of lines actually moved
3279  current_line += offset - move;
3280  // unable to read everything? Then the current line is also the line size
3281  if (move != 0)
3282  {
3283  stream_line_size = current_line;
3284  }
3285  return current_line; // return current line
3286 }
3287 
3288 
3289 /**
3290  * Seek to a specific line position
3291  *
3292  * @param offset The new target position
3293  * @param current_line
3294  * The starting line position
3295  * @param current_position
3296  * The starting character position.
3297  *
3298  * @return
3299  */
3300 int64_t StreamInfo::seekToVariableLine(int64_t offset, int64_t &current_line, int64_t &current_position)
3301 {
3302  if (current_line == offset)
3303  {
3304  return current_line;
3305  }
3306  // not possible to reach there by going forward or we don't have
3307  // valid line positioning information?
3308  if (current_line > offset || current_line <= 0)
3309  {
3310  // then read forward from the beginning
3311  current_line = 1;
3312  current_position = 1;
3313  }
3314  return readForwardByLine(offset - current_line, current_line, current_position);
3315 }
3316 
3317 
3318 /**
3319  * Make sure we have valid line positions set. If we've just
3320  * been doing character operations up to this point, our
3321  * line positions will not be set. This may require reading
3322  * the file and counting up to the positions.
3323  *
3324  * @return
3325  */
3327 {
3328  // already have information?
3329  if (lineReadPosition != 0 && lineWritePosition != 0)
3330  {
3331  // just return the
3332  return lineReadPosition;
3333  }
3334  readSetup(); // prepare to do reading
3335  // still at the beginning?
3336  if (charReadPosition == 1)
3337  {
3338  // set the line counts to match.
3339  lineReadPosition = 1;
3341  }
3342  else
3343  {
3345  {
3346  notreadyError();
3347  }
3348  // system positions are origin 0...Rexx ones are origin 1.
3350  }
3351  // now try the write position
3352  if (charWritePosition == 1)
3353  {
3354  lineWritePosition = 1;
3356  }
3357  else
3358  {
3360  {
3361  notreadyError();
3362  }
3363  // system positions are origin 0...Rexx ones are origin 1.
3365  }
3366  return lineReadPosition;
3367 }
3368 
3369 /**
3370  * Return the qualified name of a stream.
3371  *
3372  * @return The character string qualified name.
3373  */
3375 {
3376  // resolve the stream name, if necesary, and return the fully qualified
3377  // name.
3379  return qualified_name;
3380 }
3381 
3382 /********************************************************************************************/
3383 /* qualify */
3384 /********************************************************************************************/
3385 RexxMethod1(CSTRING, qualify, CSELF, streamPtr)
3386 {
3387  StreamInfo *stream_info = (StreamInfo *)streamPtr;
3388  stream_info->setContext(context, context->NullString());
3389 
3390  return stream_info->getQualifiedName();
3391 }
3392 
3393 
3394 /**
3395  * Check if a stream exists. If it does, the qualified name
3396  * is returned. This does not need to be an open stream for
3397  * this to succeed.
3398  *
3399  * @return The resolved stream name, or "" if not resolvable.
3400  */
3402 {
3403  // opened with a provided handle? We have no name.
3404  if (opened_as_handle)
3405  {
3406  return "";
3407  }
3408 
3409  // are we successfully open? We've already resolved this then
3410  if (isopen)
3411  {
3412  // is this a device of some sort? result is the original name
3413  if (fileInfo.isDevice())
3414  {
3415  return stream_name;
3416  }
3417  else // return the fully qualified stream name.
3418  {
3419  return qualified_name;
3420  }
3421  }
3422 
3423  // make sure we have the fully resolved stream name.
3426  {
3427  return qualified_name;
3428  }
3429  // can't find this, sorry.
3430  return "";
3431 }
3432 
3433 /********************************************************************************************/
3434 /* query_exists */
3435 /********************************************************************************************/
3436 RexxMethod1(CSTRING, query_exists, CSELF, streamPtr)
3437 {
3438  StreamInfo *stream_info = (StreamInfo *)streamPtr;
3439  stream_info->setContext(context, context->NullString());
3440 
3441  return stream_info->streamExists();
3442 }
3443 
3444 /**
3445  * Return the handle value for the stream.
3446  *
3447  * @return The binary handle for the stream.
3448  */
3450 {
3451  if (!isopen) /* unopened stream? */
3452  {
3453  return context->NullString();
3454  }
3455  return context->Uintptr(fileInfo.getHandle());
3456 }
3457 
3458 /********************************************************************************************/
3459 /* query_handle */
3460 /********************************************************************************************/
3461 RexxMethod1(RexxObjectPtr, query_handle, CSELF, streamPtr)
3462 {
3463  StreamInfo *stream_info = (StreamInfo *)streamPtr;
3464  stream_info->setContext(context, context->NullString());
3465  return stream_info->queryHandle();
3466 }
3467 
3468 
3469 /**
3470  * Return the type of stream (persistent or transient).
3471  *
3472  * @return String value of the type. Returns either "PERSISTENT",
3473  * "TRANSIENT", or "UNKNOWN", if the stream is not open.
3474  */
3476 {
3477  if (!isopen) /* not open? */
3478  {
3479  return "UNKNOWN"; /* don't know the type */
3480  }
3481  else if (transient)
3482  {
3483  return "TRANSIENT"; /* return the transient type */
3484  }
3485  else
3486  {
3487  return "PERSISTENT"; /* this is a persistent stream */
3488  }
3489 }
3490 
3491 /********************************************************************************************/
3492 /* query_streamtype */
3493 /********************************************************************************************/
3494 RexxMethod1(CSTRING, query_streamtype, CSELF, streamPtr)
3495 {
3496  StreamInfo *stream_info = (StreamInfo *)streamPtr;
3497 
3498  return stream_info->getStreamType();
3499 }
3500 
3501 /**
3502  * Get the size of the stream, regardless of whether it is
3503  * open or not.
3504  *
3505  * @return The int64_t size of the stream. Devices return a 0 size.
3506  */
3508 {
3509  // if we're open, return the current fstat() information,
3510  // otherwise, we do this without a handle
3511  if (isopen)
3512  {
3513  int64_t streamsize;
3514  fileInfo.getSize(streamsize);
3515  return context->Int64ToObject(streamsize);
3516  }
3517  else
3518  {
3520  int64_t streamsize;
3521  if (fileInfo.getSize(qualified_name, streamsize))
3522  {
3523  return context->Int64ToObject(streamsize);
3524  }
3525  // return a null string if this doesn't exist
3526  return context->NullString();
3527  }
3528 }
3529 
3530 /**
3531  * Get the time stamp of the stream (in character form),
3532  * regardless of whether it is open or not.
3533  *
3534  * @return A character string time stamp for the stream. Returns ""
3535  * for streams where a time stamp is meaningless.
3536  */
3538 {
3539  const char *time;
3540  // if we're open, return the current fstat() information,
3541  // otherwise, we do this without a handle
3542  if (isopen)
3543  {
3544  fileInfo.getTimeStamp(time);
3545  return time;
3546  }
3547  else
3548  {
3551  return time;
3552  }
3553 }
3554 
3555 
3556 /********************************************************************************************/
3557 /* query_size */
3558 /********************************************************************************************/
3559 RexxMethod1(RexxObjectPtr, query_size, CSELF, streamPtr)
3560 {
3561  StreamInfo *stream_info = (StreamInfo *)streamPtr;
3562  stream_info->setContext(context, context->NullString());
3563 
3564  return stream_info->getStreamSize();
3565 }
3566 
3567 
3568 /********************************************************************************************/
3569 /* query_time */
3570 /********************************************************************************************/
3571 RexxMethod1(CSTRING, query_time, CSELF, streamPtr)
3572 {
3573  StreamInfo *stream_info = (StreamInfo *)streamPtr;
3574  stream_info->setContext(context, context->NullString());
3575 
3576  return stream_info->getTimeStamp();
3577 }
3578 
3579 /**
3580  * Get the stream state as a string. This value is a constant,
3581  * and does not contain any addtional information.
3582  *
3583  * @return A string token with the stream state.
3584  */
3586 {
3587  switch (state)
3588  { /* process the different states */
3589  case StreamUnknown: /* unknown stream status */
3590  return "UNKNOWN";
3591 
3592  case StreamNotready: /* both notready and an eof condition*/
3593  case StreamEof: /* return the same thing */
3594  return "NOTREADY";
3595 
3596  case StreamError: /* had a stream error */
3597  return "ERROR";
3598 
3599  case StreamReady: /* stream is ready to roll */
3600  return "READY";
3601  }
3602  return "";
3603 }
3604 
3605 /********************************************************************************************/
3606 /* stream_state -- return state of the stream */
3607 /********************************************************************************************/
3608 RexxMethod1(CSTRING, stream_state, CSELF, streamPtr)
3609 {
3610  StreamInfo *stream_info = (StreamInfo *)streamPtr;
3611  stream_info->setContext(context, context->NullString());
3612 
3613  return stream_info->getState();
3614 }
3615 
3616 
3617 /**
3618  * Retrieve a description string for the stream.
3619  *
3620  * @return A string describing the stream state and error information.
3621  */
3623 {
3624  char work[100];
3625 
3626  switch (state) { /* process the different states */
3627 
3628  case StreamUnknown: /* unknown stream status */
3629  return context->NewStringFromAsciiz("UNKNOWN:");
3630  break;
3631 
3632  case StreamEof: /* had an end-of-file condition */
3633  return context->NewStringFromAsciiz("NOTREADY:EOF");
3634  break;
3635 
3636  case StreamNotready: /* had some sort of notready */
3637  {
3638  const char *errorString = NULL;
3639 
3640  if (errorInfo != 0)
3641  {
3642  errorString = strerror(errorInfo);
3643  }
3644 
3645  if (errorString != NULL)
3646  {
3647  /* format the result string */
3648  snprintf(work, sizeof work, "NOTREADY:%d %s", errorInfo, errorString);
3649  }
3650  else
3651  {
3652  /* format the result string */
3653  snprintf(work, sizeof work, "NOTREADY:%d", errorInfo);
3654 
3655  }
3656  return context->NewStringFromAsciiz(work);
3657  }
3658 
3659  case StreamError: /* had a stream error */
3660  {
3661  const char *errorString = NULL;
3662 
3663  if (errorInfo != 0)
3664  {
3665  errorString = strerror(errorInfo);
3666  }
3667  if (errorString != NULL)
3668  {
3669  /* format the result string */
3670  snprintf(work, sizeof work, "ERROR:%d %s", errorInfo, errorString);
3671  }
3672  else
3673  {
3674  /* format the result string */
3675  snprintf(work, sizeof work, "ERROR:%d", errorInfo);
3676 
3677  }
3678  return context->NewStringFromAsciiz(work);
3679  }
3680 
3681  case StreamReady: /* stream is ready to roll */
3682  return context->NewStringFromAsciiz("READY:");
3683  break;
3684  }
3685  return NULLOBJECT;
3686 }
3687 
3688 /********************************************************************************************/
3689 /* stream_description -- return description of the stream */
3690 /********************************************************************************************/
3691 
3692 RexxMethod1(RexxStringObject, stream_description, CSELF, streamPtr)
3693 {
3694  StreamInfo *stream_info = (StreamInfo *)streamPtr;
3695  stream_info->setContext(context, context->NullString());
3696 
3697  return stream_info->getDescription();
3698 }
3699 
3700 /**
3701  * Constructor for a StreamInfo object.
3702  *
3703  * @param s The Stream object this is attached to.
3704  * @param inputName The initial input name specified on the new call.
3705  */
3706 StreamInfo::StreamInfo(RexxObjectPtr s, const char *inputName)
3707 {
3708  self = s;
3709 
3710  // buffer needs to be allocated
3711  bufferAddress = NULL;
3712  bufferLength = 0;
3713 
3714  // initialize the default values
3715  resetFields();
3716  strncpy(stream_name, inputName, SysFileSystem::MaximumPathLength);
3717  // this stream is in an unknown state now.
3718  state = StreamUnknown;
3719 }
3720 
3721 /********************************************************************************************/
3722 /* stream_init */
3723 /********************************************************************************************/
3724 
3725 RexxMethod2(RexxObjectPtr, stream_init, OSELF, self, CSTRING, name)
3726 {
3727  // create a new stream info member
3728  StreamInfo *stream_info = new StreamInfo(self, name);
3729  RexxPointerObject streamPtr = context->NewPointer(stream_info);
3730  context->SetObjectVariable("CSELF", streamPtr);
3731 
3732  return NULLOBJECT;
3733 }
3734 
3735 /**
3736  * Count the number of lines in the file. If we already have
3737  * a pseudo line count, we can just return that.
3738  *
3739  * @param currentLinePosition
3740  * The current line position we're scanning from.
3741  * @param currentPosition
3742  * The position to start counting from.
3743  *
3744  * @return The number of lines in the file.
3745  */
3746 int64_t StreamInfo::countStreamLines(int64_t currentLinePosition, int64_t currentPosition)
3747 {
3748  // we already had to calculate this...just return the previous value.
3749  if (stream_line_size > 0)
3750  {
3751  return stream_line_size;
3752  }
3753  // go to the current position
3754  setPosition(currentPosition, currentPosition);
3755 
3756  int64_t count;
3757  // ask for the count from this point.
3758  if (!fileInfo.countLines(count))
3759  {
3760  notreadyError();
3761  }
3762 
3763  // update the position and also set the pseudo line count, since we know this now.
3764  stream_line_size = count + currentLinePosition - 1;
3765  return count;
3766 }
#define ctrl_z
Definition: SourceFile.cpp:103
@ CallItem
@ SetBool
@ SetItem
int parser(TokenDefinition *ttsp, const char *TokenString, void *userparms)
RexxMethod2(int, stream_arrayin, CSELF, streamPtr, RexxArrayObject, result)
const int position_by_line
const char * StreamInfoProperty
#define RDWR_CREAT
StreamInfo * checkStreamInfo(RexxMethodContext *context, void *streamPtr, RexxObjectPtr result)
const int query_read_position
#define IREAD_IWRITE
const int position_offset_specified
const int query_line_position
int position_offset(TokenDefinition *ttsp, StreamToken &tokenizer, void *userparms)
const int operation_nocreate
const int query_write_position
const int query_system_position
const int position_by_char
int unknown_tr(TokenDefinition *ttsp, StreamToken &tokenizer, void *userparms)
int reclength_token(TokenDefinition *ttsp, StreamToken &tokenizer, void *userparms)
RexxMethod1(int64_t, stream_chars, CSELF, streamPtr)
bool hasNoBufferOption(const char *opts)
#define WR_CREAT
const int query_char_position
const int operation_write
const int operation_read
#define CHAR_positional
RexxMethod3(RexxStringObject, stream_charin, CSELF, streamPtr, OPTIONAL_int64_t, position, OPTIONAL_size_t, read_length)
void checkStreamType()
void setLineReadPosition(int64_t position)
void resolveStreamName()
const char * getTimeStamp()
int64_t lineReadPosition
void resetFields()
int64_t charWritePosition
void setHandle(int fh)
const char * streamOpen(const char *options)
void freeBuffer()
bool open(int openFlags, int openMode, int sharedFlag)
void writeSetup()
void readSetup()
StreamInfo(RexxObjectPtr s, const char *inputName)
void implicitOpen(int type)
SysFile fileInfo
void lineReadIncrement()
RexxStringObject readVariableLine()
int64_t setLinePositions()
char * allocateBuffer(size_t length)
int64_t lineReadCharPosition
int64_t readForwardByLine(int64_t offset, int64_t &current_line, int64_t &current_position)
int64_t lineWriteCharPosition
int64_t seekLinePosition(int64_t offset, int direction, int64_t &current_line, int64_t &current_position)
const char * streamExists()
bool opened_as_handle
int64_t getLineWritePosition()
RexxMethodContext * context
char stream_name[SysFileSystem::MaximumFileNameBuffer]
void notreadyError()
const char * handleOpen(const char *options)
RexxStringObject charin(bool setPosition, int64_t position, size_t read_length)
void resetLinePositions()
void setContext(RexxMethodContext *c, RexxObjectPtr d)
int64_t charReadPosition
void writeBuffer(const char *data, size_t length, size_t &bytesWritten)
int lineout(RexxStringObject data, bool setPosition, int64_t position)
RexxObjectPtr defaultResult
void checkEof()
const char * streamFlush()
size_t binaryRecordLength
int64_t seekToVariableLine(int64_t offset, int64_t &current_line, int64_t &current_position)
const char * openStd(const char *options)
char qualified_name[SysFileSystem::MaximumFileNameLength]
int64_t getLineReadPosition()
void setCharWritePosition(int64_t position)
int64_t queryLinePosition(int64_t current_position)
RexxStringObject getDescription()
void setCharReadPosition(int64_t position)
void writeFixedLine(const char *data, size_t length)
RexxObjectPtr getStreamSize()
StreamState state
char * getDefaultBuffer(size_t &length)
int64_t countStreamLines(int64_t currentLinePosition, int64_t currentPosition)
void setLineWritePosition(int64_t position)
const char * getStreamType()
RexxObjectPtr queryHandle()
void appendVariableLine(RexxArrayObject r)
RexxObjectPtr queryStreamPosition(const char *options)
void raiseException(int err)
int64_t lineWritePosition
size_t bufferLength
RexxStringObject readLine(char *buffer, size_t length, bool update_position)
int64_t stream_line_size
void completeLine(size_t writeLength)
void setStandard()
void setPosition(int64_t position, int64_t &newPosition)
bool last_op_was_read
char * bufferAddress
const char * getState()
int64_t chars()
int64_t setLinePosition(int64_t new_line, int64_t &current_line, int64_t &current_position)
int64_t streamPosition(const char *options)
int arrayin(RexxArrayObject r)
char * extendBuffer(size_t &length)
void setWritePosition(int64_t position)
int64_t lines(bool quick)
void writeLine(const char *data, size_t length, size_t &bytesWritten)
int64_t size()
int64_t getLineSize()
RexxStringObject linein(bool setPosition, int64_t position, size_t count)
void readBuffer(char *data, size_t length, size_t &bytesRead)
size_t charout(RexxStringObject data, bool setPosition, int64_t position)
const char * getQualifiedName()
const char * streamClose()
void setReadPosition(int64_t position)
bool toNumber(int64_t &num)
void setStdIn()
bool putLine(const char *buffer, size_t len, size_t &bytesWritten)
void clearErrors()
int errorInfo()
bool atEof()
void setStdErr()
bool isDevice()
bool gets(char *buffer, size_t len, size_t &bytesRead)
bool getSize(int64_t &size)
bool seek(int64_t offset, int direction, int64_t &position)
bool seekForwardLines(int64_t startPosition, int64_t &lineCount, int64_t &endPosition)
bool flush()
bool close()
bool getPosition(int64_t &position)
uintptr_t getHandle()
void setBuffering(bool buffer, size_t length)
bool getTimeStamp(const char *&time)
bool open(const char *name, int openFlags, int openMode, int shareMode)
bool read(char *buf, size_t len, size_t &bytesRead)
bool countLines(int64_t &count)
bool write(const char *data, size_t len, size_t &bytesWritten)
bool hasData()
bool isTransient()
void setStdOut()
void reset()
bool isOpen()
static bool fileExists(const char *name)
static void qualifyStreamName(const char *unqualifiedName, char *qualifiedName, size_t bufferSize)
static void strupper(char *str)
Definition: Utilities.cpp:152
static int strCaselessCompare(const char *opt1, const char *opt2)
Definition: Utilities.cpp:102
int type
Definition: cmdparse.cpp:1888
#define argumentExists(i)
Definition: oorexxapi.h:3777
RexxObjectPtr OSELF
Definition: oorexxapi.h:4328
void * CSELF
Definition: oorexxapi.h:4329
#define argumentOmitted(i)
Definition: oorexxapi.h:3778
#define Rexx_Error_Incorrect_method_noarg
Definition: oorexxerrors.h:525
#define Rexx_Error_Incorrect_method
Definition: oorexxerrors.h:521
#define Rexx_Error_Incorrect_method_positive
Definition: oorexxerrors.h:529
#define Rexx_Error_Incorrect_method_stream_type
Definition: oorexxerrors.h:575
#define Rexx_Error_System_service
Definition: oorexxerrors.h:451
const char * CSTRING
Definition: rexx.h:78
struct _RexxStringObject * RexxStringObject
Definition: rexx.h:128
struct _RexxArrayObject * RexxArrayObject
Definition: rexx.h:130
struct _RexxBufferStringObject * RexxBufferStringObject
Definition: rexx.h:129
struct _RexxObjectPtr * RexxObjectPtr
Definition: rexx.h:127
#define NULLOBJECT
Definition: rexx.h:147
struct _RexxPointerObject * RexxPointerObject
Definition: rexx.h:132
#define RX_O_WRONLY
#define RX_S_IWRITE
#define RX_O_TRUNC
#define RX_O_APPEND
#define RX_S_IREAD
#define RX_O_RDONLY
#define RX_O_RDWR
#define RX_SH_DENYRW
#define RX_SH_DENYWR
#define RX_SH_DENYNO
#define RX_SH_DENYRD
char work[256]
signed __int64 int64_t