Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * FITS implementation of common functions | ||
3 | * Copyright (c) 2017 Paras Chadha | ||
4 | * | ||
5 | * This file is part of FFmpeg. | ||
6 | * | ||
7 | * FFmpeg is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * FFmpeg is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with FFmpeg; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #include <inttypes.h> | ||
23 | #include <limits.h> | ||
24 | #include <stdio.h> | ||
25 | #include <string.h> | ||
26 | #include "libavutil/dict.h" | ||
27 | #include "libavutil/error.h" | ||
28 | #include "libavutil/log.h" | ||
29 | #include "fits.h" | ||
30 | |||
31 | 452 | int avpriv_fits_header_init(FITSHeader *header, FITSHeaderState state) | |
32 | { | ||
33 | 452 | header->state = state; | |
34 | 452 | header->naxis_index = 0; | |
35 | 452 | header->blank_found = 0; | |
36 | 452 | header->pcount = 0; | |
37 | 452 | header->gcount = 1; | |
38 | 452 | header->groups = 0; | |
39 | 452 | header->rgb = 0; | |
40 | 452 | header->image_extension = 0; | |
41 | 452 | header->bscale = 1.0; | |
42 | 452 | header->bzero = 0; | |
43 | 452 | header->data_min_found = 0; | |
44 | 452 | header->data_max_found = 0; | |
45 | 452 | return 0; | |
46 | } | ||
47 | |||
48 | 4597 | static int dict_set_if_not_null(AVDictionary ***metadata, char *keyword, char *value) | |
49 | { | ||
50 |
2/2✓ Branch 0 taken 2398 times.
✓ Branch 1 taken 2199 times.
|
4597 | if (metadata) |
51 | 2398 | av_dict_set(*metadata, keyword, value, 0); | |
52 | 4597 | return 0; | |
53 | } | ||
54 | |||
55 | /** | ||
56 | * Extract keyword and value from a header line (80 bytes) and store them in keyword and value strings respectively | ||
57 | * @param ptr8 pointer to the data | ||
58 | * @param keyword pointer to the char array in which keyword is to be stored | ||
59 | * @param value pointer to the char array in which value is to be stored | ||
60 | * @return 0 if calculated successfully otherwise AVERROR_INVALIDDATA | ||
61 | */ | ||
62 | 5210 | static int read_keyword_value(const uint8_t *ptr8, char *keyword, char *value) | |
63 | { | ||
64 | int i; | ||
65 | |||
66 |
4/4✓ Branch 0 taken 35233 times.
✓ Branch 1 taken 319 times.
✓ Branch 2 taken 30342 times.
✓ Branch 3 taken 4891 times.
|
35552 | for (i = 0; i < 8 && ptr8[i] != ' '; i++) { |
67 | 30342 | keyword[i] = ptr8[i]; | |
68 | } | ||
69 | 5210 | keyword[i] = '\0'; | |
70 | |||
71 |
2/2✓ Branch 0 taken 4625 times.
✓ Branch 1 taken 585 times.
|
5210 | if (ptr8[8] == '=') { |
72 | 4625 | i = 10; | |
73 |
3/4✓ Branch 0 taken 9110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4485 times.
✓ Branch 3 taken 4625 times.
|
9110 | while (i < 80 && ptr8[i] == ' ') { |
74 | 4485 | i++; | |
75 | } | ||
76 | |||
77 |
1/2✓ Branch 0 taken 4625 times.
✗ Branch 1 not taken.
|
4625 | if (i < 80) { |
78 | 4625 | *value++ = ptr8[i]; | |
79 | 4625 | i++; | |
80 |
2/2✓ Branch 0 taken 749 times.
✓ Branch 1 taken 3876 times.
|
4625 | if (ptr8[i-1] == '\'') { |
81 |
3/4✓ Branch 0 taken 9063 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8314 times.
✓ Branch 3 taken 749 times.
|
9063 | for (; i < 80 && ptr8[i] != '\''; i++) { |
82 | 8314 | *value++ = ptr8[i]; | |
83 | } | ||
84 | 749 | *value++ = '\''; | |
85 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3876 times.
|
3876 | } else if (ptr8[i-1] == '(') { |
86 | ✗ | for (; i < 80 && ptr8[i] != ')'; i++) { | |
87 | ✗ | *value++ = ptr8[i]; | |
88 | } | ||
89 | ✗ | *value++ = ')'; | |
90 | } else { | ||
91 |
4/6✓ Branch 0 taken 8769 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4893 times.
✓ Branch 3 taken 3876 times.
✓ Branch 4 taken 4893 times.
✗ Branch 5 not taken.
|
8769 | for (; i < 80 && ptr8[i] != ' ' && ptr8[i] != '/'; i++) { |
92 | 4893 | *value++ = ptr8[i]; | |
93 | } | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | 5210 | *value = '\0'; | |
98 | 5210 | return 0; | |
99 | } | ||
100 | |||
101 | #define CHECK_KEYWORD(key) \ | ||
102 | if (strcmp(keyword, key)) { \ | ||
103 | av_log(avcl, AV_LOG_ERROR, "expected %s keyword, found %s = %s\n", key, keyword, value); \ | ||
104 | return AVERROR_INVALIDDATA; \ | ||
105 | } | ||
106 | |||
107 | #define CHECK_VALUE(key, val) \ | ||
108 | if (sscanf(value, "%d", &header->val) != 1) { \ | ||
109 | av_log(avcl, AV_LOG_ERROR, "invalid value of %s keyword, %s = %s\n", key, keyword, value); \ | ||
110 | return AVERROR_INVALIDDATA; \ | ||
111 | } | ||
112 | |||
113 | 5210 | int avpriv_fits_header_parse_line(void *avcl, FITSHeader *header, const uint8_t line[80], AVDictionary ***metadata) | |
114 | { | ||
115 | int dim_no, ret; | ||
116 | int64_t t; | ||
117 | double d; | ||
118 | char keyword[10], value[72], c; | ||
119 | |||
120 | 5210 | read_keyword_value(line, keyword, value); | |
121 |
6/7✓ Branch 0 taken 22 times.
✓ Branch 1 taken 177 times.
✓ Branch 2 taken 414 times.
✓ Branch 3 taken 414 times.
✓ Branch 4 taken 1124 times.
✓ Branch 5 taken 3059 times.
✗ Branch 6 not taken.
|
5210 | switch (header->state) { |
122 | 22 | case STATE_SIMPLE: | |
123 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | CHECK_KEYWORD("SIMPLE"); |
124 | |||
125 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | if (value[0] == 'F') { |
126 | ✗ | av_log(avcl, AV_LOG_WARNING, "not a standard FITS file\n"); | |
127 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | } else if (value[0] != 'T') { |
128 | ✗ | av_log(avcl, AV_LOG_ERROR, "invalid value of SIMPLE keyword, SIMPLE = %c\n", value[0]); | |
129 | ✗ | return AVERROR_INVALIDDATA; | |
130 | } | ||
131 | |||
132 | 22 | header->state = STATE_BITPIX; | |
133 | 22 | break; | |
134 | 177 | case STATE_XTENSION: | |
135 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 177 times.
|
177 | CHECK_KEYWORD("XTENSION"); |
136 | |||
137 |
2/2✓ Branch 0 taken 176 times.
✓ Branch 1 taken 1 times.
|
177 | if (!strcmp(value, "'IMAGE '")) { |
138 | 176 | header->image_extension = 1; | |
139 | } | ||
140 | |||
141 | 177 | header->state = STATE_BITPIX; | |
142 | 177 | break; | |
143 | 414 | case STATE_BITPIX: | |
144 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 414 times.
|
414 | CHECK_KEYWORD("BITPIX"); |
145 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 414 times.
|
414 | CHECK_VALUE("BITPIX", bitpix); |
146 | |||
147 |
1/2✓ Branch 0 taken 414 times.
✗ Branch 1 not taken.
|
414 | switch(header->bitpix) { |
148 | 414 | case 8: | |
149 | case 16: | ||
150 | case 32: case -32: | ||
151 | 414 | case 64: case -64: break; | |
152 | ✗ | default: | |
153 | ✗ | av_log(avcl, AV_LOG_ERROR, "invalid value of BITPIX %d\n", header->bitpix); \ | |
154 | ✗ | return AVERROR_INVALIDDATA; | |
155 | } | ||
156 | |||
157 | 414 | dict_set_if_not_null(metadata, keyword, value); | |
158 | |||
159 | 414 | header->state = STATE_NAXIS; | |
160 | 414 | break; | |
161 | 414 | case STATE_NAXIS: | |
162 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 414 times.
|
414 | CHECK_KEYWORD("NAXIS"); |
163 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 414 times.
|
414 | CHECK_VALUE("NAXIS", naxis); |
164 | 414 | dict_set_if_not_null(metadata, keyword, value); | |
165 | |||
166 |
1/2✓ Branch 0 taken 414 times.
✗ Branch 1 not taken.
|
414 | if (header->naxis) { |
167 | 414 | header->state = STATE_NAXIS_N; | |
168 | } else { | ||
169 | ✗ | header->state = STATE_REST; | |
170 | } | ||
171 | 414 | break; | |
172 | 1124 | case STATE_NAXIS_N: | |
173 | 1124 | ret = sscanf(keyword, "NAXIS%d", &dim_no); | |
174 |
2/4✓ Branch 0 taken 1124 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1124 times.
|
1124 | if (ret != 1 || dim_no != header->naxis_index + 1) { |
175 | ✗ | av_log(avcl, AV_LOG_ERROR, "expected NAXIS%d keyword, found %s = %s\n", header->naxis_index + 1, keyword, value); | |
176 | ✗ | return AVERROR_INVALIDDATA; | |
177 | } | ||
178 | |||
179 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1124 times.
|
1124 | if (sscanf(value, "%d", &header->naxisn[header->naxis_index]) != 1) { |
180 | ✗ | av_log(avcl, AV_LOG_ERROR, "invalid value of NAXIS%d keyword, %s = %s\n", header->naxis_index + 1, keyword, value); | |
181 | ✗ | return AVERROR_INVALIDDATA; | |
182 | } | ||
183 | |||
184 | 1124 | dict_set_if_not_null(metadata, keyword, value); | |
185 | 1124 | header->naxis_index++; | |
186 |
2/2✓ Branch 0 taken 414 times.
✓ Branch 1 taken 710 times.
|
1124 | if (header->naxis_index == header->naxis) { |
187 | 414 | header->state = STATE_REST; | |
188 | } | ||
189 | 1124 | break; | |
190 | 3059 | case STATE_REST: | |
191 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3056 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3059 | if (!strcmp(keyword, "BLANK") && sscanf(value, "%"SCNd64"", &t) == 1) { |
192 | 3 | header->blank = t; | |
193 | 3 | header->blank_found = 1; | |
194 |
3/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3050 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
3056 | } else if (!strcmp(keyword, "BSCALE") && sscanf(value, "%lf", &d) == 1) { |
195 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (d <= 0) |
196 | ✗ | return AVERROR_INVALIDDATA; | |
197 | 6 | header->bscale = d; | |
198 |
3/4✓ Branch 0 taken 165 times.
✓ Branch 1 taken 2885 times.
✓ Branch 2 taken 165 times.
✗ Branch 3 not taken.
|
3050 | } else if (!strcmp(keyword, "BZERO") && sscanf(value, "%lf", &d) == 1) { |
199 | 165 | header->bzero = d; | |
200 |
4/4✓ Branch 0 taken 300 times.
✓ Branch 1 taken 2585 times.
✓ Branch 2 taken 296 times.
✓ Branch 3 taken 4 times.
|
2885 | } else if (!strcmp(keyword, "CTYPE3") && !strncmp(value, "'RGB", 4)) { |
201 | 296 | header->rgb = 1; | |
202 |
4/4✓ Branch 0 taken 405 times.
✓ Branch 1 taken 2184 times.
✓ Branch 2 taken 404 times.
✓ Branch 3 taken 1 times.
|
2589 | } else if (!strcmp(keyword, "DATAMAX") && sscanf(value, "%lf", &d) == 1) { |
203 | 404 | header->data_max_found = 1; | |
204 | 404 | header->data_max = d; | |
205 |
4/4✓ Branch 0 taken 405 times.
✓ Branch 1 taken 1780 times.
✓ Branch 2 taken 404 times.
✓ Branch 3 taken 1 times.
|
2185 | } else if (!strcmp(keyword, "DATAMIN") && sscanf(value, "%lf", &d) == 1) { |
206 | 404 | header->data_min_found = 1; | |
207 | 404 | header->data_min = d; | |
208 |
2/2✓ Branch 0 taken 414 times.
✓ Branch 1 taken 1367 times.
|
1781 | } else if (!strcmp(keyword, "END")) { |
209 | 414 | return 1; | |
210 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1367 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1367 | } else if (!strcmp(keyword, "GROUPS") && sscanf(value, "%c", &c) == 1) { |
211 | ✗ | header->groups = (c == 'T'); | |
212 |
3/4✓ Branch 0 taken 349 times.
✓ Branch 1 taken 1018 times.
✓ Branch 2 taken 349 times.
✗ Branch 3 not taken.
|
1367 | } else if (!strcmp(keyword, "GCOUNT") && sscanf(value, "%"SCNd64"", &t) == 1) { |
213 |
2/4✓ Branch 0 taken 349 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 349 times.
|
349 | if (t < 0 || t > INT_MAX) |
214 | ✗ | return AVERROR_INVALIDDATA; | |
215 | 349 | header->gcount = t; | |
216 |
3/4✓ Branch 0 taken 349 times.
✓ Branch 1 taken 669 times.
✓ Branch 2 taken 349 times.
✗ Branch 3 not taken.
|
1018 | } else if (!strcmp(keyword, "PCOUNT") && sscanf(value, "%"SCNd64"", &t) == 1) { |
217 |
2/4✓ Branch 0 taken 349 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 349 times.
|
349 | if (t < 0 || t > INT_MAX) |
218 | ✗ | return AVERROR_INVALIDDATA; | |
219 | 349 | header->pcount = t; | |
220 | } | ||
221 | 2645 | dict_set_if_not_null(metadata, keyword, value); | |
222 | 2645 | break; | |
223 | } | ||
224 | 4796 | return 0; | |
225 | } | ||
226 |