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