1 /*
2  * Miscellaneous VMS-specific functions.
3  */
4 
5 #include <ctype.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <string.h>
9 
10 #include "schily/mconfig.h"
11 #include "vms_init.h"
12 #include "vms_misc.h"
13 
14 /*--------------------------------------------------------------------*/
15 
16 /*
17  * 2005-03-14 SMS.
18  * openfd_vms().
19  *
20  * VMS-specific _openfd() function.
21  *
22  * When open() (as in _openfd()) is used to open a file, and then
23  * fdopen() is used to get a FILE pointer (as in _fcons()) with "b",
24  * then the original open() must specify "ctx=bin".  Otherwise, fdopen()
25  * fails with "%SYSTEM-?-BADPARAM, bad parameter value".
26  *
27  * We use a phony O_BINARY flag bit (defined in xmconfig.h) to retain
28  * this datum, but (because it is phony) do not pass it through to
29  * open() here.
30  *
31  * The original requirement for this feature was READCD, so it makes
32  * sense to use the callback function to set the usual big-binary-file
33  * RMS parameters.
34  *
35  * If we had clues to the caller's intent, we could, where appropriate,
36  * add other performance helpers like "fop=sqo", but as-is we can't tell
37  * where it would be appropriate.
38  */
39 
40 int
openfd_vms(const char * name,int omode)41 openfd_vms(const char *name, int omode)
42 {
43 	int sts;
44 	static int open_id = 2;
45 
46 	if (omode& O_BINARY) {
47 		omode &= (~O_BINARY);
48 		sts = open(name,	/* File name. */
49 		omode,			/* Flags. */
50 		0777,			/* Mode for default protection. */
51 		"ctx=bin",		/* Binary. */
52 		"rfm=fix",		/* Fixed-length, */
53 		"mrs=512",		/* 512-byte records. */
54 		"acc", acc_cb,		/* Access callback function. */
55 		&open_id);		/* Access callback argument. */
56 	} else {
57 		sts = open(name, omode);
58 	}
59 	return (sts);
60 }
61 
62 /*--------------------------------------------------------------------*/
63 
64 /*
65  * Judge availability of str[n]casecmp() in C RTL.
66  * (Note: This must follow a "#include <decc$types.h>" in something to
67  * ensure that __CRTL_VER is as defined as it will ever be.  DEC C on
68  * VAX may not define it itself.)
69  */
70 
71 /*
72  * 2004-09-25 SMS.
73  * str[n]casecmp() replacement for old C RTL.
74  * Assumes a prehistorically incompetent toupper().
75  */
76 #ifndef HAVE_STRCASECMP
77 
78 int
strncasecmp(char * s1,char * s2,size_t n)79 strncasecmp(char *s1, char *s2, size_t n)
80 {
81 	/* Initialization prepares for n == 0. */
82 	char c1 = '\0';
83 	char c2 = '\0';
84 
85 	while (n-- > 0) {
86 		/* Set c1 and c2.  Convert l-case characters to u-case. */
87 		if (islower(c1 = *s1))
88 			c1 = toupper(c1);
89 
90 		if (islower(c2 = *s2))
91 			c2 = toupper(c2);
92 
93 		/* Quit at inequality or NUL. */
94 		if ((c1 != c2) || (c1 == '\0'))
95 			break;
96 
97 		s1++;
98 		s2++;
99 	}
100 	return ((unsigned int) c1- (unsigned int) c2);
101 }
102 
103 #ifndef UINT_MAX
104 #define	UINT_MAX	4294967295U
105 #endif
106 
107 #define	strcasecmp(s1, s2)	strncasecmp(s1, s2, UINT_MAX)
108 
109 #endif /* ndef HAVE_STRCASECMP */
110 
111 /*--------------------------------------------------------------------*/
112 
113 /*
114  * Character property table for (re-)escaping ODS5 extended file names.
115  * Note that this table ignore Unicode, and does not identify invalid
116  * characters.
117  *
118  * ODS2 valid characters: 0-9 A-Z a-z $ - _
119  *
120  * ODS5 Invalid characters:
121  *    C0 control codes (0x00 to 0x1F inclusive)
122  *    Asterisk (*)
123  *    Question mark (?)
124  *
125  * ODS5 Invalid characters only in VMS V7.2 (which no one runs, right?):
126  *    Double quotation marks (")
127  *    Backslash (\)
128  *    Colon (:)
129  *    Left angle bracket (<)
130  *    Right angle bracket (>)
131  *    Slash (/)
132  *    Vertical bar (|)
133  *
134  * Characters escaped by "^":
135  *    SP  !  #  %  &  '  (  )  +  ,  .  ;  =  @  [  ]  ^  `  {  }  ~
136  *
137  * Either "^_" or "^ " is accepted as a space.  Period (.) is a special
138  * case.  Note that un-escaped < and > can also confuse a directory
139  * spec.
140  *
141  * Characters put out as ^xx:
142  *    7F (DEL)
143  *    80-9F (C1 control characters)
144  *    A0 (nonbreaking space)
145  *    FF (Latin small letter y diaeresis)
146  *
147  * Other cases:
148  *    Unicode: "^Uxxxx", where "xxxx" is four hex digits.
149  *
150  *  Property table values:
151  *    Normal escape:    1
152  *    Space:            2
153  *    Dot:              4
154  *    Hex-hex escape:   8
155  *    -------------------
156  *    Hex digit:       64
157  */
158 
159 unsigned char char_prop[ 256] = {
160 
161 /*	NUL SOH STX ETX EOT ENQ ACK BEL   BS  HT  LF  VT  FF  CR  SO  SI */
162 	0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
163 
164 /*	DLE DC1 DC2 DC3 DC4 NAK SYN ETB  CAN  EM SUB ESC  FS  GS  RS  US */
165 	0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
166 
167 /*	SP  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /  */
168 	2,  1,  0,  1,  0,  1,  1,  1,   1,  1,  0,  1,  1,  0,  4,  0,
169 
170 /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?  */
171 	64, 64, 64, 64, 64, 64, 64, 64,  64, 64,  0,  1,  1,  1,  1,  1,
172 
173 /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O  */
174 	1, 64, 64, 64, 64, 64, 64,  0,   0,  0,  0,  0,  0,  0,  0,  0,
175 
176 /*	P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _  */
177 	0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  1,  0,  1,  1,  0,
178 
179 /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o  */
180 	1, 64, 64, 64, 64, 64, 64,  0,   0,  0,  0,  0,  0,  0,  0,  0,
181 
182 /*	p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~  DEL */
183 	0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  1,  0,  1,  1,  8,
184 
185 	8,  8,  8,  8,  8,  8,  8,  8,   8,  8,  8,  8,  8,  8,  8,  8,
186 	8,  8,  8,  8,  8,  8,  8,  8,   8,  8,  8,  8,  8,  8,  8,  8,
187 	8,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
188 	0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
189 	0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
190 	0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
191 	0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
192 	0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  8
193 };
194 
195 /*--------------------------------------------------------------------*/
196 
197 /*
198  * 2004-09-27 SMS.
199  * eat_carets().
200  *
201  * Delete ODS5 extended file name escape characters ("^") in the
202  * original buffer.
203  * Note that the current scheme does not handle all EFN cases, but it
204  * could be made more complicated.
205  */
206 
207 void
eat_carets(char * str)208 eat_carets(char *str)
209 /* char *str;	Source pointer. */
210 {
211 	char *strd;	/* Destination pointer. */
212 	char hdgt;
213 	unsigned char uchr;
214 	unsigned char prop;
215 
216 	/* Skip ahead to the first "^", if any. */
217 	while ((*str != '\0') && (*str != '^'))
218 		str++;
219 
220 	/* If no caret was found, quit early. */
221 	if (*str != '\0') {
222 		/* Shift characters leftward as carets are found. */
223 		strd = str;
224 		while (*str != '\0') {
225 			uchr = *str;
226 			if (uchr == '^') {
227 				/* Found a caret. */
228 				/*  Skip it, and check the next character. */
229 				uchr = *(++str);
230 				prop = char_prop[ uchr];
231 				if (prop& 64) {
232 					/* Hex digit.  Get char code from this and next hex digit. */
233 					if (uchr <= '9') {
234 						hdgt = uchr- '0';		/* '0' - '9' -> 0 - 9. */
235 					} else {
236 						hdgt = ((uchr- 'A')& 7)+ 10;	/* [Aa] - [Ff] -> 10 - 15. */
237 					}
238 					hdgt <<= 4;				/* X16. */
239 					uchr = *(++str);			/* Next char must be hex digit. */
240 					if (uchr <= '9') {
241 						uchr = hdgt+ uchr- '0';
242 					} else {
243 						uchr = hdgt+ ((uchr- 'A')& 15)+ 10;
244 					}
245 				} else if (uchr == '_') {
246 					/* Convert escaped "_" to " ". */
247 					uchr = ' ';
248 				} else if (uchr == '/') {
249 					/* Convert escaped "/" (invalid UNIX) to "?" (invalid VMS). */
250 					uchr = '?';
251 				}
252 				/*
253 				 * Else, not a hex digit.  Must be a simple escaped character
254 				 * (or Unicode, which is not yet handled here).
255 				 */
256 			}
257 			/* Else, not a caret.  Use as-is. */
258 			*strd = uchr;
259 
260 			/* Advance destination and source pointers. */
261 			strd++;
262 			str++;
263 		}
264 		/* Terminate the destination string. */
265 		*strd = '\0';
266 	}
267 }
268 
269 /*--------------------------------------------------------------------*/
270 
271 /*
272  * 2005-02-04 SMS.
273  * find_dir().
274  *
275  * Find directry boundaries in an ODS2 or ODS5 file spec.
276  * Returns length (zero if no directory, negative if error),
277  * and sets "start" argument to first character (typically "[") location.
278  *
279  * No one will care about the details, but the return values are:
280  *
281  *     0  No dir.
282  *    -2  [, no end.              -3  <, no end.
283  *    -4  [, multiple start.      -5  <, multiple start.
284  *    -8  ], no start.            -9  >, no start.
285  *   -16  ], wrong end.          -17  >, wrong end.
286  *   -32  ], multiple end.       -33  >, multiple end.
287  *
288  * Note that the current scheme handles only simple EFN cases, but it
289  * could be made more complicated.
290  */
291 int
find_dir(char * file_spec,char ** start)292 find_dir(char *file_spec, char **start)
293 {
294 	char *cp;
295 	char chr;
296 
297 	char *end_tmp = NULL;
298 	char *start_tmp = NULL;
299 	int lenth = 0;
300 
301 	for (cp = file_spec; cp < file_spec+ strlen(file_spec); cp++) {
302 		chr = *cp;
303 		if (chr == '^') {
304 			/* Skip ODS5 extended name escaped characters. */
305 			cp++;
306 			/* If escaped char is a hex digit, skip the second hex digit, too. */
307 			if (char_prop[ (unsigned char) *cp]& 64)
308 				cp++;
309 			} else if (chr == '[') {
310 				/* Found start. */
311 				if (start_tmp == NULL) {
312 					/* First time.  Record start location. */
313 					start_tmp = cp;
314 					/* Error if no end. */
315 					lenth = -2;
316 				} else {
317 					/* Multiple start characters.  */
318 					lenth = -4;
319 					break;
320 				}
321 			} else if (chr == '<') {
322 				/* Found start. */
323 				if (start_tmp == NULL) {
324 					/* First time.  Record start location. */
325 					start_tmp = cp;
326 					/* Error if no end. */
327 					lenth = -3;
328 				} else {
329 					/* Multiple start characters.  */
330 					lenth = -5;
331 					break;
332 				}
333 			} else if (chr == ']') {
334 				/* Found end. */
335 				if (end_tmp == NULL) {
336 					/* First time. */
337 					if (lenth == 0) {
338 						/* End without start. */
339 						lenth = -8;
340 						break;
341 					} else if (lenth != -2) {
342 						/* Wrong kind of end. */
343 						lenth = -16;
344 						break;
345 					}
346 					/* End ok.  Record end location. */
347 					end_tmp = cp;
348 					lenth = end_tmp+ 1- start_tmp;
349 					/* Could break here, ignoring excessive end characters. */
350 				} else {
351 					/* Multiple end characters. */
352 					lenth = -32;
353 					break;
354 				}
355 			} else if (chr == '>') {
356 				/* Found end. */
357 				if (end_tmp == NULL) {
358 					/* First time. */
359 					if (lenth == 0) {
360 						/* End without start. */
361 						lenth = -9;
362 						break;
363 					} else if (lenth != -3) {
364 						/* Wrong kind of end. */
365 						lenth = -17;
366 						break;
367 					}
368 					/* End ok.  Record end location. */
369 					end_tmp = cp;
370 					lenth = end_tmp+ 1- start_tmp;
371 					/* Could break here, ignoring excessive end characters. */
372 				} else {
373 					/* Multiple end characters. */
374 					lenth = -33;
375 					break;
376 				}
377 			}
378 		}
379 
380 		/* If both start and end were found, then set result pointer where safe. */
381 		if (lenth > 0) {
382 			if (start != NULL) {
383 				*start = start_tmp;
384 			}
385 		}
386 	return (lenth);
387 }
388 
389 /*--------------------------------------------------------------------*/
390