1 /*
2 ccp4_parser.c: Functions to read in and "parse" CCP4 keyworded input.
3 Copyright (C) 2001 CCLRC, Peter Briggs
4
5 This library is free software: you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public License
7 version 3, modified in accordance with the provisions of the
8 license to address the requirements of UK law.
9
10 You should have received a copy of the modified GNU Lesser General
11 Public License along with this library. If not, copies may be
12 downloaded from http://www.ccp4.ac.uk/ccp4license.php
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Lesser General Public License for more details.
18 */
19
20
21 /** @file ccp4_parser.c
22 *
23 * @brief Functions to read in and "parse" CCP4-style keyworded input.
24 *
25 * @author Peter Briggs
26 * @date April 2001
27 */
28
29 /* ccp4_parser.c
30 Peter Briggs CCP4 April 2001
31
32 Functions to read in and "parse" (scan, in reality) CCP4-style
33 "keyworded" input, plus utility routines.
34
35 ccp4_parse_start(maxtokens)
36 initialise a CCP4PARSERARRAY to be used in subsequent calls to
37 ccp4_parser routines.
38
39 ccp4_parse_end(parser)
40 clean up CCP4PARSERARRAY parser after use
41
42 ccp4_parse_init_token(parser,itok)
43 initialise a single token in CCP4PARSERARRAY before use
44
45 ccp4_parse_reset(parser)
46 initialise CCP4PARSERARRAY before use (includes calls to
47 ccp4_parse_init_token to initialise all tokens)
48
49 ccp4_parse_delimiters(parser,delimiters,nulldelimiters)
50 set up or restore non-default delimiters
51
52 int ccp4_parse_comments(parser,comment_chars)
53 set up or restore non-default comment characters
54
55 ccp4_parse_maxmin(parser,max_exp,min_exp)
56 set non-default maximum and minimum values for numerical tokens
57
58 ccp4_parse(line,parser)
59 given a string "line", break up into tokens and store in
60 CCP4PARSERARRAY "parser"
61
62 ccp4_parser(line,parser,print)
63 read input from stdin or external file, break up into tokens
64 and store in CCP4PARSERARRAY "parser"
65
66 ccp4_keymatch(keyin1,keyin2)
67 compare input strings to see if they match as CCP4-style keywords
68
69 strtoupper(str1,str2)
70 convert string to uppercase
71
72 strmatch(str1,str2)
73 check if strings are identical
74
75 charmatch(character,charlist)
76 check if character appears in a list of possibilities
77
78 doublefromstr(str,....)
79 convert a string representation of a number into the number, also
80 checks for exponent over/underflow, returns numbers of digits and
81 values of "components" (integer and fractional parts, and base-10
82 exponent)
83
84 */
85
86 /* Header files */
87
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <string.h>
91 #ifndef __FLOAT_H__
92 # include <float.h>
93 #endif
94 #include <math.h>
95 #include "ccp4_parser.h"
96 #include "ccp4_errno.h"
97 #include "ccp4_sysdep.h"
98 /* rcsid[] = "$Id$" */
99
100 /* stuff for error reporting */
101 #define CPARSER_ERRNO(n) (CCP4_ERR_PARS | (n))
102
103 /* error defs */
104 #define CPARSERR_Ok 0
105 #define CPARSERR_MaxTokExceeded 1
106 #define CPARSERR_AllocFail 2
107 #define CPARSERR_NullPointer 3
108 #define CPARSERR_LongLine 4
109 #define CPARSERR_CantOpenFile 5
110 #define CPARSERR_NoName 6
111 #define CPARSERR_ExpOverflow 7
112 #define CPARSERR_ExpUnderflow 8
113 #define CPARSERR_MatToSymop 9
114 #define CPARSERR_SymopToMat 10
115
116 /*------------------------------------------------------------------*/
117
118 /* Parser Functions */
119
120 /*------------------------------------------------------------------*/
121
122 /* ccp4_parse_start
123
124 This initialises a CCP4PARSERARRAY to be used with the ccp4_parse/
125 ccp4_parser functions.
126 The calling function must supply the maximum number of tokens.
127 */
ccp4_parse_start(const int maxtokens)128 CCP4PARSERARRAY* ccp4_parse_start(const int maxtokens)
129 {
130 int itok,diag=0;
131 CCP4PARSERARRAY *parsePtr;
132
133 if (diag) printf("CCP4_PARSE_START: ccp4_parse_start starting\n");
134
135 /* Initial check for sensible values */
136 if (maxtokens < 1) return NULL;
137
138 /* Return a pointer to a CCP4PARSERARRAY */
139 parsePtr = (CCP4PARSERARRAY *) ccp4_utils_malloc(sizeof(CCP4PARSERARRAY));
140 if (parsePtr) {
141 if (diag) printf("CCP4_PARSE_START: allocated parsePtr\n");
142 parsePtr->token = (CCP4PARSERTOKEN *) ccp4_utils_malloc(sizeof(CCP4PARSERTOKEN)*maxtokens);
143 if (!parsePtr->token) {
144 free(parsePtr);
145 parsePtr = NULL;
146 } else {
147 if (diag) printf("CCP4_PARSE_START: allocated parsePtr->token\n");
148 parsePtr->maxtokens = maxtokens;
149 parsePtr->fp = NULL;
150
151 /* Explicitly ensure that each token's fullstring member is
152 set to NULL before calling ccp4_parse_reset (which tries to
153 free memory associated with non-NULL fullstrings) since
154 we can't rely on them being created with a NULL value */
155 for (itok = 0; itok < maxtokens; itok++)
156 parsePtr->token[itok].fullstring = NULL;
157 ccp4_parse_reset(parsePtr);
158 if (diag) printf("CCP4_PARSE_START: fullstring set to NULL\n");
159
160 /* Initialise the default maximum and minimum allowed
161 exponents for numerical tokens */
162 ccp4_parse_maxmin(parsePtr,DBL_MAX_10_EXP,DBL_MIN_10_EXP);
163 if (diag) printf("CCP4_PARSE_START: max and min set\n");
164
165 /* Initialise the default delimiter and null delimiter characters
166 with a call to ccp4_parse_delimiters */
167 parsePtr->delim=NULL;
168 parsePtr->nulldelim=NULL;
169 if (!ccp4_parse_delimiters(parsePtr,NULL,NULL)) {
170 ccp4_parse_end(parsePtr);
171 parsePtr = NULL;
172 }
173 if (diag) printf("CCP4_PARSE_START: delimiters set\n");
174
175 /* Initialise the default comment characters with a call
176 to ccp4_parse_comments */
177 parsePtr->comment=NULL;
178 if (!ccp4_parse_comments(parsePtr,NULL)) {
179 ccp4_parse_end(parsePtr);
180 parsePtr = NULL;
181 }
182 if (diag) printf("CCP4_PARSE_START: comments set\n");
183 }
184 }
185
186 if (diag) printf("CCP4_PARSE_START: Returning from ccp4_parse_start\n");
187 return parsePtr;
188 }
189
190 /*------------------------------------------------------------------*/
191
192 /* ccp4_parse_end
193
194 This cleans up a CCP4PARSEARRAY after being used by ccp4_parse/
195 ccp4_parser functions.
196 */
ccp4_parse_end(CCP4PARSERARRAY * parsePtr)197 int ccp4_parse_end(CCP4PARSERARRAY *parsePtr)
198 {
199 int i,maxtokens;
200
201 /* Anything to do? */
202 if (parsePtr) {
203 /* Free memory for each token */
204 maxtokens = parsePtr->maxtokens;
205 if (parsePtr->token && parsePtr->maxtokens > 0) {
206 for (i=0; i<maxtokens; i++)
207 if(parsePtr->token[i].fullstring) free(parsePtr->token[i].fullstring);
208 free(parsePtr->token);
209 }
210 /* Free memory for lists of comments and delimiters */
211 if (parsePtr->comment) free(parsePtr->comment);
212 if (parsePtr->delim) free(parsePtr->delim);
213 if (parsePtr->nulldelim) free(parsePtr->nulldelim);
214 /* Free memory for rest of parserarray structure */
215 free(parsePtr);
216 }
217 /* All done */
218 return 0;
219 }
220
221 /*------------------------------------------------------------------*/
222
223 /* ccp4_parse_init_token
224
225 Initialise a token in a parser array
226 This sets all string members of the specified token to NULL and
227 all numerical values (including flags) to zero
228 */
229
ccp4_parse_init_token(const CCP4PARSERARRAY * parsePtr,const int itok)230 int ccp4_parse_init_token(const CCP4PARSERARRAY *parsePtr, const int itok)
231 {
232 if (parsePtr) {
233 if (itok < parsePtr->maxtokens) {
234 /* Full string is dynamically allocated - free the
235 associated memory, if assigned */
236 if (parsePtr->token[itok].fullstring) {
237 free(parsePtr->token[itok].fullstring);
238 parsePtr->token[itok].fullstring = NULL;
239 }
240 /* Set fixed string tokens to empty string */
241 strcpy(parsePtr->token[itok].word,"");
242 /* Set numerical value to zero */
243 parsePtr->token[itok].value = 0.0;
244 /* Set flags to zero */
245 parsePtr->token[itok].isstring = 0;
246 parsePtr->token[itok].isnumber = 0;
247 parsePtr->token[itok].isquoted = 0;
248 parsePtr->token[itok].isnull = 0;
249 /* Set start and end positions to zero */
250 parsePtr->token[itok].ibeg = 0;
251 parsePtr->token[itok].iend = 0;
252 }
253 }
254 return 0;
255 }
256
257 /*------------------------------------------------------------------*/
258
259 /* ccp4_parse_reset
260
261 Reinitialise a parser array before calling ccp4_parse
262
263 An application using ccp4_parse (rather than ccp4_parser, which
264 also calls this function) can call this function to reset the
265 parser array, rather than reinitialising the structure members
266 explicitly
267 */
ccp4_parse_reset(CCP4PARSERARRAY * parsePtr)268 int ccp4_parse_reset(CCP4PARSERARRAY *parsePtr)
269 {
270 int itok;
271
272 if (parsePtr) {
273 /* Initialise the tokens to have null values */
274 for (itok=0; itok<parsePtr->maxtokens; itok++)
275 ccp4_parse_init_token(parsePtr,itok);
276 /* Initialise number of tokens to zero */
277 parsePtr->ntokens = 0;
278 }
279 return 0;
280 }
281
282 /*------------------------------------------------------------------*/
283
284 /* ccp4_parse_delimiters
285
286 This allows the application to set its own delimiter characters
287 to be used in the ccp4_parser routines.
288
289 If a NULL pointer is supplied for either of the two lists then
290 then the default delimiters are (re)set.
291
292 Returns 1 on success, 0 if there was an error. In the event of
293 an error the delimiter lists will be unchanged.
294 */
295
ccp4_parse_delimiters(CCP4PARSERARRAY * parsePtr,const char * delim,const char * nulldelim)296 int ccp4_parse_delimiters(CCP4PARSERARRAY *parsePtr,
297 const char *delim, const char *nulldelim)
298 {
299 const char defdelim[]=" \t,=\r",defnulldelim[]=",=";
300 char *delimPtr=NULL,*nulldelimPtr=NULL;
301 int ldelim,lnulldelim,istatus=1;
302
303 if (parsePtr) {
304
305 /* If supplied delim is NULL then set to the default */
306 if (!delim) {
307 ldelim = strlen(defdelim) + 1;
308 } else {
309 ldelim = strlen(delim) + 1;
310 }
311 delimPtr = (char *) ccp4_utils_malloc(sizeof(char)*ldelim);
312 if (delimPtr) {
313 ldelim--;
314 if (!delim) {
315 strncpy(delimPtr,defdelim,ldelim+1);
316 } else {
317 strncpy(delimPtr,delim,ldelim+1);
318 }
319 delimPtr[ldelim] = '\0';
320 }
321
322 /* If supplied nulldelim is NULL then set to the default */
323 if (!nulldelim) {
324 lnulldelim = strlen(defnulldelim) + 1;
325 } else {
326 lnulldelim = strlen(nulldelim) + 1;
327 }
328 nulldelimPtr = (char *) ccp4_utils_malloc(sizeof(char)*lnulldelim);
329 if (nulldelimPtr) {
330 lnulldelim--;
331 if (!nulldelim) {
332 strncpy(nulldelimPtr,defnulldelim,lnulldelim+1);
333 } else {
334 strncpy(nulldelimPtr,nulldelim,lnulldelim+1);
335 }
336 nulldelimPtr[lnulldelim] = '\0';
337 }
338
339 /* Assign new delimiters in parser array */
340 if (delimPtr && nulldelimPtr) {
341 if (parsePtr->delim) free(parsePtr->delim);
342 parsePtr->delim = delimPtr;
343 if (parsePtr->nulldelim) free(parsePtr->nulldelim);
344 parsePtr->nulldelim = nulldelimPtr;
345 } else {
346 /* There is an error - don't reset the parser array */
347 if (delimPtr) free(delimPtr);
348 if (nulldelimPtr) free(nulldelimPtr);
349 istatus = 0;
350 }
351 } else {
352 istatus = 0;
353 }
354 return istatus;
355 }
356
357 /*------------------------------------------------------------------*/
358
359 /* ccp4_parse_comments
360
361 This allows the application to set its own comment characters
362 to be used in the ccp4_parser routines.
363
364 If a NULL pointer is supplied for the list of comment characters
365 then the default comment characters are (re)set.
366
367 Returns 1 on success, 0 if there was an error. In the event of
368 an error the comment lists will be unchanged.
369 */
370
ccp4_parse_comments(CCP4PARSERARRAY * parsePtr,const char * comment_chars)371 int ccp4_parse_comments(CCP4PARSERARRAY *parsePtr, const char *comment_chars)
372 {
373 const char def_comment_chars[]="#!";
374 char *commentPtr=NULL;
375 int lcomment,istatus=1;
376
377 if (parsePtr) {
378
379 /* If the supplied comment list is NULL then set to the default */
380 if (!comment_chars) {
381 lcomment = strlen(def_comment_chars) + 1;
382 } else {
383 lcomment = strlen(comment_chars) + 1;
384 }
385 commentPtr = (char *) ccp4_utils_malloc(sizeof(char)*lcomment);
386 if (commentPtr) {
387 if (!comment_chars) {
388 strncpy(commentPtr,def_comment_chars,lcomment);
389 } else {
390 strncpy(commentPtr,comment_chars,lcomment);
391 }
392 lcomment--;
393 commentPtr[lcomment] = '\0';
394 }
395
396 /* Assign the new comments in the parser array */
397 if (commentPtr) {
398 if (parsePtr->comment) free(parsePtr->comment);
399 parsePtr->comment = commentPtr;
400 } else {
401 /* There was an error - don't reset the parser array */
402 istatus = 0;
403 }
404 } else {
405 /* The parser was unset on entry - also an error */
406 istatus = 0;
407 }
408 return istatus;
409 }
410
411 /*------------------------------------------------------------------*/
412
413 /* ccp4_parse_maxmin
414
415 This allows the application to set its own maximum and minimum
416 exponent values, which are used as limits when evaluating the values
417 of numerical tokens in order to avoid over/underflow.
418 */
419
ccp4_parse_maxmin(CCP4PARSERARRAY * parsePtr,const double max_exponent,const double min_exponent)420 int ccp4_parse_maxmin(CCP4PARSERARRAY *parsePtr, const double max_exponent,
421 const double min_exponent)
422 {
423 if (parsePtr) {
424 parsePtr->max_exponent = (double) max_exponent;
425 parsePtr->min_exponent = (double) min_exponent;
426 }
427 return 1;
428 }
429
430 /*------------------------------------------------------------------*/
431
432 /* ccp4_parse
433
434 This is a scanner based on the old CCP4 Fortranic PARSE routine.
435
436 It takes an input string ("line") and returns the number of
437 tokens ("ntokens") which are delimited by certain characters
438 (defaulted to space, tab, comma, equals - these can be changed by
439 the application using a call to ccp4_parse_delimiters).
440 Information about the tokens themselves is returned as members of
441 elements in an array ("tokens") of type CCP4PARSERTOKEN (see header
442 file for definition and members).
443
444 Substrings can be delimited by single- or double-quotes but must
445 be surrounded by delimiters to be recognised.
446
447 An unquoted comment character (defaulted to ! or #) in the input line
448 introduces a trailing comment which is ignored. The comment
449 characters can be changed using a call to ccp4_parse_comments.
450
451 Null fields are denoted by two adjacent null delimiters (defaulted
452 to comma and equals - these can be changed by the application
453 using a call to ccp4_parse_delimiters).
454
455 ccp4_parse returns the number of tokens found in the line. The
456 tokens are returned via the CCP4PARSERARRAY parser.
457
458 Arguments:
459
460 line = pointer to a null-terminated string of characters,
461 forming the input to be processed. Unaltered on
462 output.
463 parser = pointer to a CCP4PARSERARRAY structure which will
464 be used to hold the results of processing the input
465 line.
466 */
467
ccp4_parse(const char * line,CCP4PARSERARRAY * parser)468 int ccp4_parse(const char *line, CCP4PARSERARRAY *parser)
469 {
470 int quotedstring,starttoken,endtoken;
471 char this_char,next_char,matchquote;
472
473 int llen,ich,lword,diag=0;
474 int token,nulltoken,isquote,iscommt=0,isdelim;
475 double value;
476 char *delim,*nulldelim,*comm;
477 char quot[]="\"\'";
478 int ibeg,iend,start;
479
480 double intvalue,frcvalue,expvalue;
481 int intdigits,frcdigits,expdigits;
482
483 /* Local parser variables */
484 int ntok,maxtok;
485 CCP4PARSERTOKEN *tokenarray;
486
487 /* Begin */
488
489 /* Set diag = 1 and recompile to switch on diagnostic output */
490 if (diag) printf("CCP4_PARSE: ccp4_parse starting\n");
491
492 maxtok = parser->maxtokens;
493 ntok = parser->ntokens;
494 if (ntok < 0) ntok = 0;
495
496 /* Initialise pointer for local version of the token array */
497 tokenarray = parser->token;
498
499 /* Initialise pointers for lists of comments and delimiters */
500 comm = parser->comment;
501 delim = parser->delim;
502 nulldelim = parser->nulldelim;
503
504 /* Don't process any tokens already dealt with */
505 if (ntok > 0) {
506 start = tokenarray[ntok-1].iend + 1;
507 /* Special case: if the last token was quoted
508 then in fact the character at this position will be
509 a quote - if so then step on another character */
510 if (charmatch(line[start],quot)) {
511 if (diag) printf("CCP4_PARSE: start character is a quote, stepping on\n");
512 start++;
513 }
514 } else {
515 start = 0;
516 }
517
518 /* Don't process empty lines */
519 llen = strlen(line);
520 if (diag) printf("CCP4_PARSE: Line is: \"%s\"\nLength of line is %d\n",line,llen);
521 if (llen > 0) {
522
523 /* Initialise flags and counters */
524 quotedstring = 0;
525 token = 0;
526 nulltoken = 0;
527
528 /* Process the line two characters at a time */
529
530 if (diag) printf("CCP4_PARSE: Start position for parsing line is %d\n",start);
531 for (ich=start-1; ich<llen; ich++) {
532
533 /* Examine characters in pairs */
534
535 /* Deal with special case, start and end of line
536 This is to avoid accessing non-existant parts of the string */
537 if (ich < start) {
538 this_char = delim[0];
539 } else {
540 this_char = line[ich];
541 }
542 if (ich == llen-1) {
543 next_char = delim[0];
544 } else {
545 next_char = line[ich+1];
546 }
547 if (diag) printf("CCP4_PARSE: %d: Current char = \"%c\" : \n",ich,this_char);
548
549 /* Set flags for this round
550 The pairs of characters are analysed and flags set
551 accordingly to signal actions after the analysis */
552 starttoken = 0;
553 endtoken = 0;
554
555 if (!quotedstring) {
556 /* Not in a quoted string so look for opening quotes
557 This is delimiter followed by quote character */
558
559 /* Is current character a delimiter character? */
560 isdelim = charmatch(this_char,delim);
561
562 /* Is next charcter a quote character? */
563 isquote = charmatch(next_char,quot);
564
565 /* Start of quoted string? */
566 if (isdelim && isquote) {
567
568 if (diag) printf("CCP4_PARSE: start of a quoted string\n");
569 quotedstring = 1;
570 starttoken = 1;
571 matchquote = next_char;
572 /* Advance to the next character immediately */
573 ich++;
574
575 } else {
576 /* Not the start of a quoted string */
577
578 /* Is the current character the start of a comment? */
579 iscommt = charmatch(this_char,comm);
580 if (diag)
581 printf("CCP4_PARSE: character = %c comments = %s iscommt = %d\n",
582 this_char,comm,iscommt);
583 if (iscommt) {
584 if (diag) printf("CCP4_PARSE: start of a comment\n");
585 }
586
587 /* Is the current character the start or end of a token? */
588 if (!isdelim) {
589
590 /* End of the current token?
591 Only if we are in a token now, and the next
592 character is a delimiter or a comment */
593 if (token && (charmatch(next_char,delim) || charmatch(next_char,comm))) {
594 if (diag) printf("CCP4_PARSE: end of a token\n");
595 endtoken = 1;
596 }
597
598 } else {
599
600 /* Start of a token?
601 Only if we are not in a token now, and the next character
602 is not a delimiter (or a comment) too */
603 if (!token && !(charmatch(next_char,delim) || charmatch(next_char,comm))) {
604 if (diag) printf("CCP4_PARSE: start of a token\n");
605 starttoken = 1;
606 }
607
608 /* Or - could be a null token?
609 This is a pair of null delimiters together */
610 if (!token && charmatch(this_char,nulldelim)
611 && charmatch(next_char,nulldelim)) {
612 if (diag) printf("CCP4_PARSE: null token\n");
613 starttoken = 1;
614 nulltoken = 1;
615 }
616
617 }
618 /* End of token identification */
619 }
620
621 } else {
622 /* Inside a quoted string so look for closing quotes
623 This is a matching quote followed by a delimiter */
624
625 /* Is current character a matching quote? */
626 isquote = (this_char == matchquote);
627
628 /* Is next charcter a delimiter character? */
629 isdelim = charmatch(next_char,delim);
630
631 /* End of quoted string */
632 if (isdelim && isquote) {
633 if (diag) printf("CCP4_PARSE: end of a quoted string\n");
634 quotedstring = 0;
635 endtoken = 1;
636 matchquote = ' ';
637 }
638 /* End of character analyses */
639 }
640
641 /* Start of new token */
642 if (starttoken) {
643 /* Check we don't have maximum number of tokens already */
644 if (ntok < maxtok) {
645 /* Set flags */
646 token = 1;
647 if (quotedstring || !nulltoken) {
648 ibeg = ich + 1;
649 } else {
650 ibeg = ich;
651 }
652 if (diag) printf("CCP4_PARSE: Start of a new token... ibeg = %d\n",ibeg);
653 /* Initialise values */
654 tokenarray[ntok].fullstring = NULL;
655 tokenarray[ntok].value = 0.0;
656 tokenarray[ntok].isstring = 0;
657 tokenarray[ntok].strlength = 0;
658 tokenarray[ntok].isnumber = 0;
659 tokenarray[ntok].intdigits= 0;
660 tokenarray[ntok].frcdigits= 0;
661 tokenarray[ntok].isquoted = 0;
662 tokenarray[ntok].isnull = 0;
663 /* Flag start of quoted string */
664 if (quotedstring) { tokenarray[ntok].isquoted = 1; }
665 /* Flag null token */
666 if (nulltoken) {
667 if (diag) printf("CCP4_PARSE: Null token\n");
668 tokenarray[ntok].isnull = 1;
669 tokenarray[ntok].iend = ich;
670 token = 0;
671 nulltoken = 0;
672 }
673 } else {
674 /* Maximum number of tokens already found */
675 ccp4_signal(CPARSER_ERRNO(CPARSERR_MaxTokExceeded),"ccp4_parse",NULL);
676 parser->ntokens = ntok;
677 return ntok;
678 }
679 if (diag) printf("CCP4_PARSE: This is the start of token %d\n",ntok);
680 }
681 /* End of new token */
682
683 /* End of current token */
684 if (endtoken) {
685 token = 0;
686 /* Exclude trailing quote from the token? */
687 if (tokenarray[ntok].isquoted) {
688 iend = ich - 1;
689 } else {
690 iend = ich;
691 }
692 if (diag) printf("CCP4_PARSE: End of a token... iend = %d\n",iend);
693 /* Store the full token in the array */
694 lword = iend - ibeg + 1;
695 if (diag) printf("CCP4_PARSE: lword = %d - start char = %c, end char = %c\n",
696 lword,line[ibeg],line[iend]);
697 tokenarray[ntok].fullstring = (char *) ccp4_utils_malloc(sizeof(char)*(lword+1));
698 if (tokenarray[ntok].fullstring) {
699 strncpy(tokenarray[ntok].fullstring,&line[ibeg],lword);
700 tokenarray[ntok].fullstring[lword] = '\0';
701 if (diag) printf("CCP4_PARSE: Token is \"%s\"\n",tokenarray[ntok].fullstring);
702 } else {
703 ccp4_signal(CPARSER_ERRNO(CPARSERR_AllocFail),"ccp4_parse",NULL);
704 }
705 tokenarray[ntok].ibeg = ibeg;
706 tokenarray[ntok].iend = iend;
707 /* Store the 4 character token in the array */
708 if (lword > 4) lword = 4;
709 strncpy(tokenarray[ntok].word,&line[ibeg],lword);
710 tokenarray[ntok].word[lword] = '\0';
711 /* Determine numerical value (if any) */
712 if (doublefromstr(tokenarray[ntok].fullstring,parser->max_exponent,
713 parser->min_exponent,&value,&intvalue,&intdigits,
714 &frcvalue,&frcdigits,&expvalue,&expdigits)) {
715 if (diag) printf("CCP4_PARSE: This has a numerical value of %lf\n",value);
716 tokenarray[ntok].value = value;
717 tokenarray[ntok].isnumber = 1;
718 tokenarray[ntok].intdigits = intdigits;
719 tokenarray[ntok].frcdigits = frcdigits;
720 } else {
721 if (diag) printf("CCP4_PARSE: There is no numerical value for this token\n");
722 tokenarray[ntok].isstring = 1;
723 tokenarray[ntok].strlength = strlen(tokenarray[ntok].fullstring);
724 }
725 /* Reset flags etc ready for next token*/
726 token = 0;
727 value = 0.0;
728 /* Increment number of tokens */
729 ntok++;
730 if (diag) printf("CCP4_PARSE: This is the end of a token\n");
731 }
732
733 /* Don't do any more processing after a comment */
734
735 if (iscommt) {
736 parser->ntokens = ntok;
737 if (diag) printf("CCP4_PARSE: returning after a comment\n");
738 return ntok;
739 }
740 /* Check the next pair of characters */
741 }
742 /* Reset the number of tokens in the parser array */
743 parser->ntokens = ntok;
744 if (diag) printf("CCP4_PARSE: ntokens = %d, and ntok = %d\n",parser->ntokens,ntok);
745 }
746 if (diag) printf("CCP4_PARSE: returning at function end\n");
747 return ntok;
748 }
749
750 /*------------------------------------------------------------------*/
751
752 /* ccp4_parser
753
754 This is based on the old CCP4 Fortranic PARSER routine.
755
756 The normal behaviour is to read "keyworded" data from the input
757 stream, and interpret it. Stdin is the default, but a line
758 starting with @<name> starts reading from file <name> until eof.
759
760 Each input line may be continued on the next line by the continuation
761 characters `&', `-' or `\' at the end of the input line. This
762 character is dropped from the list returned to the calling application.
763
764 Pass in a zero length line to force reading from the command line.
765 nchars is the maximum number of characters which will be read into the line.
766 (If line is not blank then it will be processed and more input
767 read in if it ends in a continuation character, or forces reading from
768 an external file.)
769
770 The "print" argument should be supplied as 0 to suppress echoing of the
771 input lines to standard output.
772
773 ccp4_parser returns the number of tokens parsed in the input line. The
774 results of the parsing are stored as members of the CCP4PARSEARRAY
775 structure "parser" and can be accessed by the application program.
776
777 The function returns the number of tokens, or 0 on reaching end of file.
778 On encountering an unrecoverable error ccp4_parser returns -1.
779
780 Arguments:
781
782 line = pointer to a null-terminated string of characters,
783 forming the input to be processed.
784 On input can either be an empty string ("") or
785 contain characters to be processed (see above for
786 description).
787 On output "line" will be overwritten with the actual
788 input line, up to nchar characters.
789 nchars = maximum number of characters that can be read into
790 "line" i.e. the size of "line" in memory.
791 parser = pointer to a CCP4PARSERARRAY structure which will
792 be used to hold the results of processing the input
793 line.
794 print = flag controlling echoing of input lines to stdout.
795 print=0: suppress echoing of lines to stdout
796 Otherwise echoing is turned on.
797 */
798
ccp4_parser(char * line,const int nchars,CCP4PARSERARRAY * parser,const int print)799 int ccp4_parser(char *line, const int nchars, CCP4PARSERARRAY *parser,
800 const int print)
801 {
802 int fromstdin=0,fromfile=0,fromapp=0,diag=0;
803 int nch,nold,continuation,first,trunc,llen,buflen;
804 char *linein=NULL,filename[200];
805
806 /* Local parser variables */
807 int ntok;
808 FILE *filein;
809 CCP4PARSERTOKEN *tokenarray;
810
811 /* Undocumented feature - if print < 0 then also print out
812 diagnostic info */
813 if (print < 0) {
814 /*print = 1;*/
815 diag = 1;
816 }
817
818 /* Begin */
819 if (diag) printf("CCP4_PARSER: ccp4_parser starting\n");
820
821 /* Abort if parser is a NULL pointer */
822 if (!parser) {
823 ccp4_signal(CPARSER_ERRNO(CPARSERR_NullPointer),"ccp4_parser",NULL);
824 return -1;
825 }
826
827 /* Abort if line is NULL pointer */
828 if (!line) {
829 ccp4_signal(CPARSER_ERRNO(CPARSERR_NullPointer),"ccp4_parser",NULL);
830 return -1;
831 }
832
833 /* Reset the parser array for this sweep
834 This will prevent phantom values from an earlier call
835 to ccp4_parser persisting in the parser array */
836 ccp4_parse_reset(parser);
837
838 /* Blank the keyword */
839 strcpy(parser->keyword,"");
840
841 /* Initialise local variables and pointers */
842 tokenarray = parser->token;
843 ntok = parser->ntokens;
844 filein = parser->fp;
845 if (diag) printf("CCP4_PARSER: parser->ntokens = %d, ntok = %d\n",
846 parser->ntokens,ntok);
847
848 /* Set up an internal buffer for input
849 The buffer is over-allocated (twice as long as the max string
850 length allocated for line by the calling application)
851 */
852 buflen = (nchars*2)+1;
853 linein = (char *) ccp4_utils_malloc(buflen*sizeof(char));
854
855 if (!linein) {
856 ccp4_signal(CPARSER_ERRNO(CPARSERR_AllocFail),"ccp4_parser",NULL);
857 return 0;
858 }
859
860 /* Use nch as a count of the number of remaining characters
861 in line */
862 nch = nchars;
863
864 /* If line is empty then read from the standard input
865 Otherwise process the line from the application first */
866 if (strlen(line)==0) {
867 if (!filein) {
868 if (diag) printf("CCP4_PARSER: Reading from stdin\n");
869 fromstdin = 1;
870 } else {
871 if (diag) printf("CCP4_PARSER: Reading from file\n");
872 fromfile = 1;
873 }
874 } else {
875 if (diag) printf("CCP4_PARSER: Reading line supplied by the application program\n");
876 fromapp = 1;
877 }
878
879 /* Set flag for first line of input */
880 first = 1;
881
882 /* Set flag for line continuation */
883 continuation = 1;
884
885 /* Start the input loop */
886 while (continuation) {
887
888 if (diag) printf("CCP4_PARSER: starting loop\n");
889 /* Read input from stdin a line at a time */
890 if (fromstdin) {
891 if (diag) printf("CCP4_PARSER: reading from stdin...\n");
892 if (!fgets(linein,buflen,stdin)) {
893 /* Jump out at this point if eof is reached from
894 stdin */
895 return 0;
896 }
897 } else if (fromfile) {
898 if (diag) printf("CCP4_PARSER: reading from external file...\n");
899 if (!fgets(linein,buflen,filein)) {
900 /* Return to input from stdin if eof is read from
901 the external file */
902 if (diag) printf("CCP4_PARSER: End of external file reached\n");
903 fclose(filein);
904 filein = NULL;
905 fromfile = 0;
906 fromstdin = 1;
907 /* Blank the line and reset the first flag to
908 force reading from standard input immediately */
909 linein[0] = '\0';
910 ntok = 0;
911 parser->ntokens = ntok;
912 first = 1;
913 }
914 } else if (fromapp) {
915 if (diag) printf("CCP4_PARSER: reading from application...\n");
916 /* If this line contains a continuation mark then
917 read from stdin next time around */
918 strncpy(linein,line,nchars);
919 linein[nchars]='\0';
920 }
921
922 /* Strip any trailing newline e.g. from fgets */
923 llen = strlen(linein);
924 if (llen > 0)
925 if (linein[llen-1] == '\n') {
926 linein[llen-1] = '\0';
927 llen--;
928 }
929
930 /* If previous line ended with a continuation character
931 then append this one to it
932 Check that we don't overflow the number of characters
933 specified by the application */
934
935 if (llen > nch) {
936 ccp4_signal(CPARSER_ERRNO(CPARSERR_LongLine),"ccp4_parser",NULL);
937 }
938 if (first) {
939 strncpy(line,linein,nch);
940 first = 0;
941 } else {
942 strncat(line,linein,nch);
943 }
944 nch = nchars - llen;
945 if (diag) {
946 printf("CCP4_PARSER: line = \"%s\"\n",line);
947 printf("CCP4_PARSER: remaining available characters = %d\n",nch);
948 }
949
950 /* Use ccp4_parse to break the input line up into tokens
951 Only parse the latest chunk - ccp4_parse will append
952 new tokens onto the tokenarray */
953 nold = ntok;
954 ntok = ccp4_parse(line,parser);
955
956 if (diag) printf("CCP4_PARSER: ntok = %d, nold = %d\n",ntok,nold);
957
958 /* Have we found more tokens since last time? */
959 if (ntok != nold) {
960 /* Check first token to see if it is an instruction
961 to read from an external file */
962 if (!fromfile && tokenarray[0].word[0] == '@') {
963 if (diag) printf("CCP4_PARSER: Instruction to read from external file\n");
964 /* Get filename and attempt to open file */
965 if (tokenarray[0].fullstring) {
966 llen = strlen(tokenarray[0].fullstring);
967 strncpy(filename,&tokenarray[0].fullstring[1],llen);
968 if (diag) printf("CCP4_PARSER: External file name is \"%s\"\n",filename);
969 /* Open the external file as read-only */
970 filein = fopen(filename,"r");
971 if (!filein) {
972 ccp4_signal(CPARSER_ERRNO(CPARSERR_CantOpenFile),"ccp4_parser",NULL);
973 } else {
974 fromstdin = 0;
975 fromfile = 1;
976 }
977 } else {
978 /* Token with file name is null */
979 ccp4_signal(CPARSER_ERRNO(CPARSERR_NoName),"ccp4_parser",NULL);
980 }
981 /* Blank the line and reset the number of tokens
982 to force reading from the external file immediately */
983 line[0] = '\0';
984 ntok = 0;
985 parser->ntokens = ntok;
986
987 /* Check last token to see if it is continuation
988 character */
989 } else if (ntok > 0 &&
990 (strmatch("&",tokenarray[ntok-1].word) ||
991 strmatch("\\",tokenarray[ntok-1].word) ||
992 strmatch("-",tokenarray[ntok-1].word))) {
993 if (diag) printf("CCP4_PARSER: Detected continuation character\n");
994 /* It's a continuation mark
995 Set flag to indicate this fact in later rounds */
996 continuation = 1;
997 /* Truncate the line to remove the continuation
998 character */
999 if (ntok > 1)
1000 trunc = tokenarray[ntok-1].ibeg;
1001 else
1002 trunc = 0;
1003 if (diag) printf("CCP4_PARSER: Continuation character should be at position %d\n\"%c\" is the character at this position\n",trunc,line[trunc]);
1004 line[trunc] = '\0';
1005 /* Lose the last token */
1006 ntok--;
1007 parser->ntokens = ntok;
1008 } else {
1009 /* Not a continuation character */
1010 continuation = 0;
1011 }
1012
1013 } else {
1014 /* Didn't get any more tokens from the last pass
1015 Check if it is a blank line or comment line */
1016 if (ntok == 0) {
1017 /* Echo comment line to stdout and blank
1018 the line */
1019 if (strlen(line) > 0) {
1020 if (print) printf(" Comment line--- %s\n",line);
1021 line[0] = '\0';
1022 nch = nchars;
1023 }
1024 if (fromapp) continuation = 0;
1025 }
1026 }
1027 if (diag) printf("CCP4_PARSER: Continuation = %d\n",continuation);
1028
1029 /* If the line was supplied by the application but is now being continued
1030 then make sure we read from stdin next time */
1031 if (continuation && fromapp) {
1032 if (diag) printf("CCP4_PARSER: switching to stdin\n");
1033 fromapp = 0;
1034 fromstdin = 1;
1035 }
1036 }
1037
1038 /* Fetch and uppercase keyword */
1039 if (ntok > 0) {
1040 strtoupper(parser->keyword,tokenarray[0].word);
1041 parser->keyword[strlen(tokenarray[0].word)] = '\0';
1042 if (diag) printf("CCP4_PARSER: Keyword is %s\n",parser->keyword);
1043 /*Echo the line to standard output */
1044 if (print) printf(" Data line--- %s\n",line);
1045 } else {
1046 parser->keyword[0] = '\0';
1047 }
1048
1049 free(linein);
1050 /* Update the returned variables */
1051 parser->fp = filein;
1052
1053 if (diag) printf("CCP4_PARSER: Returning from ccp4_parser\n");
1054 return ntok;
1055 }
1056
1057 /*------------------------------------------------------------------*/
1058
1059 /* ccp4_keymatch
1060
1061 Returns 1 if keywords keyin1 and keyin2 are "identical", 0 otherwise.
1062
1063 Keywords are identical if they are the same up to the first four
1064 characters, independent of case.
1065 */
ccp4_keymatch(const char * keyin1,const char * keyin2)1066 int ccp4_keymatch(const char *keyin1, const char *keyin2)
1067 {
1068 int len1,len2;
1069 char key1[5],key2[5],keyup1[5],keyup2[5];
1070
1071 /* Initial check */
1072 if (!keyin1 || !keyin2) return 0;
1073
1074 /* Compare truncated lengths */
1075 len1 = strlen(keyin1);
1076 if (len1 > 4) len1 = 4;
1077
1078 len2 = strlen(keyin2);
1079 if (len2 > 4) len2 = 4;
1080
1081 /* If lengths don't match then keywords can't be identical */
1082 if (len1 != len2) return 0;
1083
1084 /* If supplied words are longer than four characters then
1085 truncate them after the fourth character */
1086 strncpy(key1,keyin1,len1);
1087 key1[len1] = '\0';
1088
1089 strncpy(key2,keyin2,4);
1090 key2[len2] = '\0';
1091
1092 /* Convert strings to uppercase */
1093 strtoupper(keyup1,key1);
1094 keyup1[len1] = '\0';
1095 strtoupper(keyup2,key2);
1096 keyup2[len2] = '\0';
1097
1098 /* Compare using strmatch */
1099 return strmatch(keyup1,keyup2);
1100 }
1101
strtoupper(char * str1,const char * str2)1102 char *strtoupper (char *str1, const char *str2)
1103 {
1104 int len2,i;
1105
1106 if (!str2) return NULL;
1107
1108 len2 = strlen(str2);
1109 if (len2 > 0) for (i=0; i<len2 ; i++) str1[i] = toupper(str2[i]);
1110 str1[len2] = '\0';
1111 return str1;
1112 }
1113
strtolower(char * str1,const char * str2)1114 char *strtolower (char *str1, const char *str2)
1115 {
1116 int len2,i;
1117
1118 if (!str2) return NULL;
1119
1120 len2 = strlen(str2);
1121 if (len2 > 0) for (i=0; i<len2 ; i++) str1[i] = tolower(str2[i]);
1122 str1[len2] = '\0';
1123 return str1;
1124 }
1125
1126 /*------------------------------------------------------------------*/
1127
1128 /* strmatch
1129
1130 Compare two strings.
1131
1132 Returns 1 if strings are identical, 0 if they differ.
1133 */
strmatch(const char * str1,const char * str2)1134 int strmatch (const char *str1, const char *str2)
1135 {
1136 int len1,len2,i;
1137
1138 /* Don't process null strings */
1139 if (!str1 || !str2) return 0;
1140
1141 len1 = strlen(str1);
1142 len2 = strlen(str2);
1143
1144 /* Cannot be identical if lengths differ */
1145 if (len1 != len2) return 0;
1146
1147 /* Check character by character
1148 If any character differs then strings cannot be identical */
1149 for (i=0; i<len1; i++)
1150 if (str1[i] != str2[i]) return 0;
1151
1152 /* Must be a match */
1153 return 1;
1154 }
1155
1156 /*------------------------------------------------------------------*/
1157
1158 /* charmatch
1159
1160 Returns 1 if character matches one of the characters in the string,
1161 0 otherwise.
1162 */
charmatch(const char character,const char * charlist)1163 int charmatch(const char character, const char *charlist)
1164 {
1165 int jdo,ismatch=0;
1166
1167 if (charlist) {
1168 jdo = 0;
1169 while (jdo<strlen(charlist) && !ismatch) {
1170 if (charlist[jdo] == character) ismatch = 1;
1171 jdo++;
1172 }
1173 }
1174
1175 return ismatch;
1176 }
1177
1178 /*------------------------------------------------------------------*/
1179
1180 /* doublefromstr
1181
1182 Determine whether string represents a valid number, and return the
1183 numerical value if it is.
1184 The function returns 1 for a valid number, 0 otherwise.
1185
1186 Valid numbers are represented by:
1187 ^[+-]?[0-9]*\.?[0-9]*[Ee]?[+-]?[0-9]*$
1188 (I think this is the correct regular expression...? -pjx)
1189
1190 The component parts are: an integer part representing the value
1191 of the number upto the decimal point; a decimal fraction
1192 representing the value of the number upto the base-10 exponent;
1193 a base-10 exponent. The full value of the string is then
1194 int_part + frac_part * 10^(exp_part)
1195
1196 The value of each component is returned separately via the argument
1197 list. The number of "digits" (= number of characters in the string)
1198 of each component are also returned - note that these include non-
1199 significant digits such as leading zeroes in the integer part,
1200 but non-numeric characters (+-eE) are not counted.
1201
1202 The function can also trap for large or small exponents, to
1203 avoid overflow or underflow.
1204 The maximum and minimum allowed exponents are supplied by the calling
1205 application as the arguments max_exp and min_exp. (Values can be
1206 be taken from float.h).
1207 In the event of the limits being exceeded, only the values of the
1208 components are evaluated; the total value of the number is returned as
1209 zero.
1210 */
doublefromstr(const char * str,const double max_exp,const double min_exp,double * valuePtr,double * intvaluePtr,int * intdigitsPtr,double * frcvaluePtr,int * frcdigitsPtr,double * expvaluePtr,int * expdigitsPtr)1211 int doublefromstr(const char *str, const double max_exp, const double min_exp,
1212 double *valuePtr, double *intvaluePtr, int *intdigitsPtr,
1213 double *frcvaluePtr, int *frcdigitsPtr,
1214 double *expvaluePtr, int *expdigitsPtr)
1215 {
1216 int lstr,ichar=0,char_value,diag=0;
1217 int sign,expsign,point,exponent,is_int,is_frc,is_exp;
1218 int n_int_digits=0,n_frc_digits=0,n_exp_digits=0;
1219 char this_char, this_str[2];
1220 double int_value=0,frc_value=0,exp_value=0;
1221
1222 if (diag) printf("DOUBLEFROMSTR: starting\n");
1223 if (diag) printf("DOUBLEFROMSTR: Biggest exponent is %lf, smallest is %lf\n",max_exp,min_exp);
1224
1225 /* Initialise exported variables */
1226 *valuePtr = 0.0;
1227 *intvaluePtr = 0.0;
1228 *frcvaluePtr = 0.0;
1229 *expvaluePtr = 0.0;
1230 *intdigitsPtr = 0;
1231 *frcdigitsPtr = 0;
1232 *expdigitsPtr = 0;
1233
1234 /* Nothing to do for an empty string */
1235 lstr = strlen(str);
1236 if (!lstr) return 0;
1237
1238 /* Initialise internal flags and variables */
1239 sign = 1;
1240 expsign = 1;
1241 point = -1;
1242 exponent = -1;
1243
1244 is_int = 1;
1245 is_frc = 0;
1246 is_exp = 0;
1247
1248 /* Process the string character by character */
1249 while (ichar < lstr) {
1250 /* Get current character */
1251 this_char = str[ichar];
1252 if (diag) printf("DOUBLEFROMSTR: This char = %c",this_char);
1253
1254 /* Check: is this character a digit? */
1255 if (!isdigit(this_char)) {
1256
1257 /* Not a digit */
1258 if (diag) printf(" is not a digit...\n");
1259
1260 if (this_char == '+' || this_char == '-') {
1261 /* Sign? i.e. + or -
1262 This can only occur in two places: immediately at the
1263 start of the putative number, or immediately at the start
1264 of the putative exponent */
1265 if (ichar == 0 && this_char == '-') {
1266 sign = -1;
1267 } else if (ichar == exponent + 1 && this_char == '-') {
1268 expsign = -1;
1269 } else if (this_char != '+') {
1270 return 0;
1271 }
1272
1273 } else if (this_char == '.') {
1274 /* Decimal point? i.e. .
1275 There can only be one decimal point */
1276 if (point > -1) return 0;
1277 point = ichar;
1278 is_int = 0;
1279 is_frc = 1;
1280
1281 } else if (toupper(this_char) == 'E') {
1282 char next_char = (ichar+1 < lstr ) ? str[ichar+1] : '\0';
1283 if ( next_char == '+' || next_char == '-')
1284 next_char = (ichar+2 < lstr ) ? str[ichar+2] : '\0';
1285 /* require the next active character after E to be a digit */
1286 if ( !isdigit(next_char) ) return 0;
1287 /* Exponent? i.e. e or E
1288 There can only be one exponent */
1289 if (exponent > -1) return 0;
1290 exponent = ichar;
1291 is_int = 0;
1292 is_frc = 0;
1293 is_exp = 1;
1294 } else {
1295 /* Not a permissible character
1296 This is not a number so get out now */
1297 if (diag) printf("DOUBLEFROMSTR: Not a permitted character - exiting\n");
1298 return 0;
1299 }
1300 } else {
1301 /* It is a digit
1302 Build up the value of each component */
1303
1304 if (diag) printf(" is a digit ...\n");
1305
1306 this_str[0] = this_char;
1307 this_str[1] = '\0';
1308 char_value = atoi(this_str);
1309
1310 if (is_int) {
1311 /* Integer part of the number */
1312 n_int_digits++;
1313 int_value = int_value * 10.0 + (double) char_value;
1314 if (diag) printf("DOUBLEFROMSTR: Processing integer component: value = %lf, #digits = %d\n",int_value,n_int_digits);
1315 } else if (is_frc) {
1316 /* Decimal part of the number */
1317 n_frc_digits++;
1318 frc_value = frc_value + ((double) char_value)/pow(10.0,(double) n_frc_digits);
1319 if (diag) printf("DOUBLEFROMSTR: Processing decimal component: value = %lf, #digits = %d\n",frc_value,n_frc_digits);
1320 } else if (is_exp) {
1321 /* Exponent */
1322 n_exp_digits++;
1323 exp_value = exp_value * 10.0 + (double) char_value;
1324 if (diag) printf("DOUBLEFROMSTR: Processing exponential component: value = %lf, #digits = %d\n",exp_value,n_exp_digits);
1325 }
1326
1327 }
1328 /* Next character */
1329 ichar++;
1330 }
1331 /*
1332 Done loop over characters - if we have got this far then it
1333 must be a number
1334 */
1335
1336 /* Set component values */
1337 int_value = int_value * (double) sign;
1338 frc_value = frc_value * (double) sign;
1339 exp_value = exp_value * (double) expsign;
1340
1341 /* Export component values */
1342 *intvaluePtr = int_value;
1343 *frcvaluePtr = frc_value;
1344 *expvaluePtr = exp_value;
1345
1346 /* Export numbers of 'digits' */
1347 *intdigitsPtr = n_int_digits;
1348 *frcdigitsPtr = n_frc_digits;
1349 *expdigitsPtr = n_exp_digits;
1350
1351 /* Is the exponent out-of-range? */
1352
1353 /* There are two considerations:
1354 (i) can pow(10.0,exp_value) actually be evaluated?
1355 (ii) can int_part * pow(10.0,exp_value) be evaluated?
1356 This second is an issue for numbers with int_part > 0.
1357 */
1358 if ( (exp_value + (double) (n_int_digits - 1) > max_exp) &&
1359 (n_int_digits || n_frc_digits) ) {
1360 ccp4_signal(CPARSER_ERRNO(CPARSERR_ExpOverflow),"doublefromstr",NULL);
1361 printf("DOUBLEFROMSTR: Token is \"%s\"\n",str);
1362 *valuePtr = 0.0;
1363 } else if ( (exp_value < min_exp) &&
1364 (n_int_digits || n_frc_digits) ) {
1365 ccp4_signal(CPARSER_ERRNO(CPARSERR_ExpUnderflow),"doublefromstr",NULL);
1366 printf("DOUBLEFROMSTR: Token is \"%s\"\n",str);
1367 *valuePtr = 0.0;
1368 } else {
1369 /* Evaluate the number to get a value */
1370 *valuePtr = int_value + frc_value;
1371 if (is_exp) *valuePtr = (*valuePtr)*pow(10.0,exp_value);
1372 }
1373
1374 if (diag) printf("DOUBLEFROMSTR: Integer component = %lf, (%d digits)\n",
1375 *intvaluePtr,*intdigitsPtr);
1376 if (diag) printf("DOUBLEFROMSTR: Decimal component = %lf, (%d digits)\n",
1377 *frcvaluePtr,*frcdigitsPtr);
1378 if (diag) printf("DOUBLEFROMSTR: Exponent component = %lf, (%d digits)\n",
1379 *expvaluePtr,*expdigitsPtr);
1380 if (diag) printf("DOUBLEFROMSTR: Finished - value is determined to be %lf\n",*valuePtr);
1381
1382 return 1;
1383 }
1384
symop_to_rotandtrn(const char * symchs_begin,const char * symchs_end)1385 ccp4_symop symop_to_rotandtrn(const char *symchs_begin, const char *symchs_end) {
1386
1387 float rsm[4][4];
1388
1389 symop_to_mat4(symchs_begin, symchs_end, rsm[0]);
1390 return (mat4_to_rotandtrn((const float (*)[4])rsm));
1391
1392 }
1393
1394 /*------------------------------------------------------------------*/
1395
1396 /* symop_to_mat4
1397
1398 Translates a single symmetry operator string into a 4x4 quine
1399 matrix representation
1400 NB Uses a utility function (symop_to_mat4_err) when reporting
1401 failures.
1402
1403 Syntax of possible symop strings:
1404
1405 real space symmetry operations, e.g. X+1/2,Y-X,Z
1406 reciprocal space operations, e.g. h,l-h,-k
1407 reciprocal axis vectors, e.g. a*+c*,c*,-b*
1408 real space axis vectors, e.g. a,c-a,-b
1409
1410 The strings can contain spaces, and the coordinate and translation
1411 parts may be in either order.
1412
1413 The function returns 1 on success, 0 if there was a failure to
1414 generate a matrix representation.
1415 */
symop_to_mat4(const char * symchs_begin,const char * symchs_end,float * rot)1416 const char *symop_to_mat4(const char *symchs_begin, const char *symchs_end, float *rot)
1417 {
1418 int no_real =0, no_recip = 0, no_axis = 0; /* counters */
1419 int col = 3, nops = 0;
1420 int nsym = 0, init_array = 1;
1421 float sign = 1.0f, value = 0.0f, value2;
1422 char *cp, ch;
1423 const char *ptr_symchs = symchs_begin;
1424 int j,k; /* loop variables */
1425 int Isep = 0; /* parsed seperator? */
1426
1427 while (ptr_symchs < symchs_end) {
1428 ch = *ptr_symchs;
1429
1430 /* Parse symop */
1431 if (isspace(ch)) {
1432 /* Have to allow symop strings to contain spaces for
1433 compatibility with older MTZ files
1434 Ignore and step on to next character */
1435 ++ptr_symchs;
1436 continue;
1437 } else if (ch == ',' || ch == '*') {
1438 ++ptr_symchs;
1439 if (value == 0.0f && col == 3) {
1440 /* nothing set, this is a problem */
1441 ccp4_signal(CPARSER_ERRNO(CPARSERR_SymopToMat),"symop_to_mat4",NULL);
1442 return NULL ;
1443 } else {
1444 Isep = 1; /* drop through to evaluation*/
1445 }
1446 } else if (ch == 'X' || ch == 'x') {
1447 no_real++, col = 0;
1448 if (value == 0.0f) value = sign * 1.0f;
1449 ++ptr_symchs;
1450 continue;
1451 } else if (ch == 'Y' || ch == 'y') {
1452 no_real++, col = 1;
1453 if (value == 0.0f) value = sign * 1.0f;
1454 ++ptr_symchs;
1455 continue;
1456 } else if (ch == 'Z' || ch == 'z') {
1457 no_real++, col = 2;
1458 if (value == 0.0f) value = sign * 1.0f;
1459 ++ptr_symchs;
1460 continue;
1461 } else if (ch == 'H' || ch == 'h') {
1462 no_recip++, col = 0;
1463 if (value == 0.0f) value = sign * 1.0f;
1464 ++ptr_symchs;
1465 continue;
1466 } else if (ch == 'K' || ch == 'k') {
1467 no_recip++, col = 1;
1468 if (value == 0.0f) value = sign * 1.0f;
1469 ++ptr_symchs;
1470 continue;
1471 } else if (ch == 'L' || ch == 'l') {
1472 no_recip++, col = 2;
1473 if (value == 0.0f) value = sign * 1.0f;
1474 ++ptr_symchs;
1475 continue;
1476 } else if (ch == 'A' || ch == 'a') {
1477 no_axis++, col = 0;
1478 if (value == 0.0f) value = sign * 1.0f;
1479 if (*++ptr_symchs == '*' && ( no_axis != 3 || no_recip )) ++ptr_symchs;
1480 continue;
1481 } else if (ch == 'B' || ch == 'b') {
1482 no_axis++, col = 1;
1483 if (value == 0.0f) value = sign * 1.0f;
1484 if (*++ptr_symchs == '*' && ( no_axis != 3 || no_recip )) ++ptr_symchs;
1485 continue;
1486 } else if (ch == 'C' || ch == 'c') {
1487 no_axis++, col = 2;
1488 if (value == 0.0f) value = sign * 1.0f;
1489 if (*++ptr_symchs == '*' && ( no_axis != 3 || no_recip )) ++ptr_symchs;
1490 continue;
1491 } else if (ch == '+' || ch == '-') {
1492 sign = ((ch == '+')? 1.0f : -1.0f) ;
1493 ++ptr_symchs;
1494 if ( value == 0.0f && col == 3)
1495 continue;
1496 /* drop through to evaluation */
1497 } else if ( ch == '/') {
1498 ++ptr_symchs;
1499 if (value == 0.0f) {
1500 /* error */
1501 symop_to_mat4_err(symchs_begin);
1502 return NULL;
1503 }
1504 value2 = strtod(ptr_symchs, &cp);
1505 if (!value2) {
1506 /* error */
1507 symop_to_mat4_err(symchs_begin);
1508 return NULL;
1509 }
1510 /* Nb don't apply the sign to value here
1511 It will already have been applied in the previous round */
1512 value = (float) value/value2;
1513 ptr_symchs = cp;
1514 continue;
1515 } else if ( isdigit(ch) || ch == '.') {
1516 value = sign*strtod(ptr_symchs, &cp);
1517 ptr_symchs = cp;
1518 continue;
1519 } else {
1520 ++ptr_symchs;
1521 continue;
1522 }
1523
1524 /* initialise and clear the relevant array (init_array == 1)*/
1525 /* use knowledge that we are using a [4][4] for rot */
1526 if (init_array) {
1527 init_array = 0;
1528 for (j = 0 ; j !=4 ; ++j)
1529 for (k = 0; k !=4 ; ++k)
1530 rot[(((nsym << 2) + k ) << 2) +j] = 0.0f;
1531 rot[(((nsym << 2 ) + 3 ) << 2) +3] = 1.0f;
1532 }
1533
1534 /* value to be entered in rot */
1535 rot[(((nsym << 2) + nops) << 2) + col] = value;
1536
1537 /* have we passed a operator seperator */
1538 if (Isep) {
1539 Isep = 0;
1540 ++nops;
1541 sign = 1.0f;
1542 if (nops == 3 ) { ++nsym; nops=0 ; init_array = 1; }
1543 }
1544
1545 /* reset for next cycle */
1546 col = 3;
1547 value = 0.0f;
1548 no_recip = 0, no_axis = 0, no_real = 0;
1549 }
1550
1551 /* Tidy up last value */
1552 if (value) rot[(((nsym << 2) + nops) << 2) + col] = value;
1553
1554 if (nops<2) {
1555 /* Processed fewer than 3 operators, raise an error */
1556 symop_to_mat4_err(symchs_begin);
1557 return NULL;
1558 }
1559
1560 /* Return with success */
1561 return ptr_symchs;
1562 }
1563
1564 /* Internal function: report error from symop_to_mat4_err */
symop_to_mat4_err(const char * symop)1565 int symop_to_mat4_err(const char *symop)
1566 {
1567 printf("\n **SYMMETRY OPERATOR ERROR**\n\n Error in interpreting symop \"%s\"\n\n",
1568 symop);
1569 ccp4_signal(CPARSER_ERRNO(CPARSERR_SymopToMat),"symop_to_mat4",NULL);
1570 return 1;
1571 }
1572
mat4_to_rotandtrn(const float rsm[4][4])1573 ccp4_symop mat4_to_rotandtrn(const float rsm[4][4]) {
1574
1575 int i,j;
1576 ccp4_symop symop;
1577
1578 for (i = 0; i < 3; ++i) {
1579 for (j = 0; j < 3; ++j)
1580 symop.rot[i][j]=rsm[i][j];
1581 symop.trn[i]=rsm[i][3];
1582 }
1583
1584 return (symop);
1585 }
1586
rotandtrn_to_symop(char * symchs_begin,char * symchs_end,const ccp4_symop symop)1587 char *rotandtrn_to_symop(char *symchs_begin, char *symchs_end, const ccp4_symop symop)
1588 {
1589 float rsm[4][4];
1590
1591 rotandtrn_to_mat4(rsm,symop);
1592 return(mat4_to_symop(symchs_begin,symchs_end,(const float (*)[4])rsm));
1593 }
1594
rotandtrn_to_mat4(float rsm[4][4],const ccp4_symop symop)1595 void rotandtrn_to_mat4(float rsm[4][4], const ccp4_symop symop) {
1596
1597 int i,j;
1598
1599 for (i = 0; i < 3; ++i) {
1600 for (j = 0; j < 3; ++j)
1601 rsm[i][j]=symop.rot[i][j];
1602 rsm[i][3]=symop.trn[i];
1603 rsm[3][i]=0.0;
1604 }
1605 rsm[3][3]=1.0;
1606 }
1607
mat4_to_symop(char * symchs_begin,char * symchs_end,const float rsm[4][4])1608 char *mat4_to_symop(char *symchs_begin, char *symchs_end, const float rsm[4][4])
1609 {
1610 static char axiscr[] = {'X','Y','Z'};
1611 static char numb[] = {'0','1','2','3','4','5','6','7','8','9'};
1612 static int npntr1[12] = { 0,1,1,1,0,1,0,2,3,5,0,0 };
1613 static int npntr2[12] = { 0,6,4,3,0,2,0,3,4,6,0,0 };
1614
1615 int jdo10, jdo20, irsm, itr, ist;
1616 register char *ich;
1617 int debug=0;
1618
1619 if (debug)
1620 for (jdo20 = 0; jdo20 != 4; ++jdo20)
1621 printf("Input matrix: %f %f %f %f \n",rsm[jdo20][0],rsm[jdo20][1],
1622 rsm[jdo20][2],rsm[jdo20][3]);
1623
1624 /* blank output string */
1625 for (ich = symchs_begin; ich < symchs_end; ++ich)
1626 *ich = ' ';
1627 ich = symchs_begin;
1628
1629 for (jdo20 = 0; jdo20 != 3; ++jdo20) {
1630 *ich = '0';
1631 ist = 0; /* ---- Ist is flag for first character of operator */
1632 for (jdo10 = 0; jdo10 != 4; ++jdo10) {
1633
1634 if (rsm[jdo20][jdo10] != 0.f) {
1635 irsm = (int) rint(fabs(rsm[jdo20][jdo10]));
1636
1637 if ( rsm[jdo20][jdo10] > 0. && ist) {
1638 if (ich >= symchs_end) {
1639 ccp4_signal(CCP4_ERRLEVEL(3) | CPARSER_ERRNO(CPARSERR_MatToSymop),
1640 "mat4_to_symop 1", NULL);
1641 return NULL; }
1642 *ich++ = '+';
1643 } else if ( rsm[jdo20][jdo10] < 0.f ) {
1644 if (ich >= symchs_end) {
1645 ccp4_signal(CCP4_ERRLEVEL(3) | CPARSER_ERRNO(CPARSERR_MatToSymop),
1646 "mat4_to_symop 2", NULL);
1647 return NULL; }
1648 if (jdo10 != 3) {
1649 *ich++ = '-';
1650 } else {
1651 /* translation part is forced to be positive, see below */
1652 *ich++ = '+';
1653 }
1654 ist = 1;
1655 }
1656
1657 if (jdo10 != 3) {
1658 /* rotation part */
1659 if (ich+1 >= symchs_end) {
1660 ccp4_signal(CCP4_ERRLEVEL(3) | CPARSER_ERRNO(CPARSERR_MatToSymop),
1661 "mat4_to_symop 3", NULL);
1662 return NULL; }
1663 if (irsm != 1) {
1664 *ich++ = numb[irsm];
1665 *ich++ = axiscr[jdo10];
1666 } else {
1667 *ich++ = axiscr[jdo10];
1668 }
1669 ist = 1;
1670 } else {
1671 /* translation part */
1672 itr = (int) rint(rsm[jdo20][3]*12.0);
1673 while (itr < 0) itr += 12;
1674 itr = (itr - 1) % 12;
1675 if (npntr1[itr] > 0) {
1676 if (ich+2 >= symchs_end) {
1677 ccp4_signal(CCP4_ERRLEVEL(3) | CPARSER_ERRNO(CPARSERR_MatToSymop),
1678 "mat4_to_symop 4", NULL);
1679 return NULL; }
1680 *ich++ = numb[npntr1[itr]];
1681 *ich++ = '/';
1682 *ich++ = numb[npntr2[itr]];
1683 } else {
1684 *--ich = ' ';
1685 }
1686 }
1687 }
1688 }
1689 if (jdo20 != 2) {
1690 if (*ich == '0')
1691 ++ich;
1692 if (ich+2 >= symchs_end) {
1693 ccp4_signal(CCP4_ERRLEVEL(3) | CPARSER_ERRNO(CPARSERR_MatToSymop),
1694 "mat4_to_symop 5", NULL);
1695 return NULL; }
1696 *ich++ = ',';
1697 *ich++ = ' ';
1698 *ich++ = ' ';
1699 }
1700 }
1701 return symchs_begin;
1702 }
1703
mat4_to_recip_symop(char * symchs_begin,char * symchs_end,const float rsm[4][4])1704 char *mat4_to_recip_symop(char *symchs_begin, char *symchs_end, const float rsm[4][4])
1705 {
1706 char *symop;
1707 size_t lsymop;
1708 register char *ich, *ich_out;
1709
1710 lsymop = symchs_end-symchs_begin;
1711 symop = (char *) ccp4_utils_malloc(lsymop*sizeof(char));
1712
1713 mat4_to_symop(symop, symop+lsymop, rsm);
1714 ich_out = symchs_begin;
1715 for (ich = symop; ich < symop+lsymop; ++ich) {
1716 if (*ich == 'X') {
1717 if (ich_out == symchs_begin || (ich_out > symchs_begin &&
1718 *(ich_out-1) != '-' && *(ich_out-1) != '+')) *ich_out++ = '+';
1719 *ich_out++ = 'h';
1720 } else if (*ich == 'Y') {
1721 if (ich_out == symchs_begin || (ich_out > symchs_begin &&
1722 *(ich_out-1) != '-' && *(ich_out-1) != '+')) *ich_out++ = '+';
1723 *ich_out++ = 'k';
1724 } else if (*ich == 'Z') {
1725 if (ich_out == symchs_begin || (ich_out > symchs_begin &&
1726 *(ich_out-1) != '-' && *(ich_out-1) != '+')) *ich_out++ = '+';
1727 *ich_out++ = 'l';
1728 } else if (*ich == ' ') {
1729 /* skip */
1730 } else {
1731 *ich_out++ = *ich;
1732 }
1733 }
1734 while (ich_out < symchs_end) *ich_out++ = ' ';
1735
1736 free (symop);
1737 return symchs_begin;
1738 }
1739