1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1993-2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <math.h>
32 
33 #include <Audio.h>
34 #include <AudioHdr.h>
35 
36 #include <parse.h>
37 #include <convert.h>
38 
39 static struct keyword_table Keywords[] = {
40 	(char *)"encoding",		K_ENCODING,
41 	(char *)"rate",			K_RATE,
42 	(char *)"channels",		K_CHANNELS,
43 	(char *)"offset",		K_OFFSET,
44 	(char *)"format",		K_FORMAT,
45 	NULL,				K_NULL,
46 };
47 
48 // Lookup the string in a keyword table. return the token associated with it.
49 keyword_type
do_lookup(char * s,struct keyword_table * kp)50 do_lookup(
51 	char			*s,
52 	struct keyword_table	*kp)
53 {
54 	struct keyword_table	*tkp = NULL;
55 
56 	for (; kp && kp->name; kp++) {
57 		if (strncmp(s, kp->name, strlen(s)) == 0) {
58 			// check if exact match
59 			if (strlen(s) == strlen(kp->name)) {
60 				return (kp->type);
61 			} else {
62 				// already have another partial match, so
63 				// it's ambiguous
64 				if (tkp) {
65 					return (K_AMBIG);
66 				} else {
67 					tkp = kp;
68 				}
69 			}
70 		}
71 	}
72 
73 	// at end of list. if there was a partial match, return it, if
74 	// not, there's no match....
75 	if (tkp) {
76 		return (tkp->type);
77 	} else {
78 		return (K_NULL);
79 	}
80 }
81 
82 // Parse a file format specification
83 int
fileformat_parse(char * val,format_type & format)84 fileformat_parse(
85 	char		*val,
86 	format_type&	format)
87 {
88 	// XXX - other formats later ...
89 	if (strcasecmp(val, "sun") == 0) {
90 		format = F_SUN;
91 	} else if (strcasecmp(val, "raw") == 0) {
92 		format = F_RAW;
93 	} else if (strcasecmp(val, "aiff") == 0) {
94 		Err(MGET("AIFF not yet supported\n"));
95 		return (-1);
96 	} else {
97 		return (-1);
98 	}
99 	return (0);
100 }
101 
102 // Parse an audio format keyword
103 int
audioformat_parse(char * val,AudioHdr & hdr)104 audioformat_parse(
105 	char		*val,
106 	AudioHdr&	hdr)
107 {
108 	// check if it's "cd" or "dat" or "voice".
109 	// these set the precision and encoding, etc.
110 	if (strcasecmp(val, "dat") == 0) {
111 		hdr.sample_rate = 48000;
112 		hdr.channels = 2;
113 		hdr.encoding = LINEAR;
114 		hdr.samples_per_unit = 1;
115 		hdr.bytes_per_unit = 2;
116 	} else if (strcasecmp(val, "cd") == 0) {
117 		hdr.sample_rate = 44100;
118 		hdr.channels = 2;
119 		hdr.encoding = LINEAR;
120 		hdr.samples_per_unit = 1;
121 		hdr.bytes_per_unit = 2;
122 	} else if (strcasecmp(val, "voice") == 0) {
123 		hdr.sample_rate = 8000;
124 		hdr.channels = 1;
125 		hdr.encoding = ULAW;
126 		hdr.samples_per_unit = 1;
127 		hdr.bytes_per_unit = 1;
128 	} else {
129 		return (-1);
130 	}
131 	return (0);
132 }
133 
134 // Parse a format spec and return an audio header that describes it.
135 // Format is in the form of: [keyword=]value[,[keyword=]value ...].
136 int
parse_format(char * s,AudioHdr & hdr,format_type & format,off_t & offset)137 parse_format(
138 	char		*s,
139 	AudioHdr&	hdr,
140 	format_type&	format,
141 	off_t&		offset)
142 {
143 	char		*cp;
144 	char		*buf;
145 	char		*key;
146 	char		*val;
147 	char		*cp2;
148 
149 	offset = 0;
150 	format = F_SUN;
151 
152 	// if no string provided, just return ...
153 	if (!(s && *s))
154 		return (0);
155 
156 	// First off, try to parse it as a full format string
157 	// (it would have to have been quoted).
158 	// If this works, we're done.
159 	if (hdr.FormatParse(s) == AUDIO_SUCCESS) {
160 		return (0);
161 	}
162 
163 	buf = strdup(s);	// save a copy of the string
164 
165 	// XXX - bug alert: if someone has info="xxx,yyy", strtok will
166 	// break unless we snarf properly snarf the info. punt for now,
167 	// fix later (since no info supported yet)....
168 
169 	for (cp = strtok(buf, ","); cp; cp = strtok(NULL, ",")) {
170 		// Check if there's a '='
171 		// If so, left side is keyword, right side is value.
172 		// If not, entire string is value.
173 		if (cp2 = strchr(cp, '=')) {
174 			*cp2++ = '\0';
175 			key = cp;
176 			val = cp2;
177 
178 			// Look for the keyword
179 			switch (do_lookup(key, Keywords)) {
180 			case K_ENCODING:
181 				if (hdr.EncodingParse(val)) {
182 					Err(MGET(
183 					    "invalid encoding option: %s\n"),
184 					    val);
185 					goto parse_error;
186 				}
187 				break;
188 			case K_RATE:
189 				if (hdr.RateParse(val)) {
190 					Err(MGET("invalid sample rate: %s\n"),
191 					    val);
192 					goto parse_error;
193 				}
194 				break;
195 			case K_CHANNELS:
196 				if (hdr.ChannelParse(val)) {
197 					Err(MGET(
198 					    "invalid channels option: %s\n"),
199 					    val);
200 					goto parse_error;
201 				}
202 				break;
203 			case K_FORMAT:
204 				if (fileformat_parse(val, format) < 0) {
205 					Err(MGET("unknown format: %s\n"), val);
206 					goto parse_error;
207 				}
208 				break;
209 			case K_OFFSET:
210 				offset = (off_t)atoi(val);
211 				break;
212 			case K_AMBIG:
213 				Err(MGET("ambiguous keyword: %s\n"), key);
214 				goto parse_error;
215 			case K_NULL:
216 				Err(MGET("null keyword: =%s\n"), val);
217 				goto parse_error;
218 			default:
219 				Err(MGET("invalid keyword: %s\n"), key);
220 				goto parse_error;
221 			}
222 		} else {
223 			// No keyword, so try to intuit the value
224 			// First try encoding, audio, and file format.
225 			// If they fail, try sample rate and channels.
226 			val = cp;
227 			if (hdr.EncodingParse(val) &&
228 			    (audioformat_parse(val, hdr) < 0) &&
229 			    (fileformat_parse(val, format) < 0)) {
230 				// If this looks like sample rate, make sure
231 				// it is not ambiguous with channels
232 				if (!hdr.RateParse(val)) {
233 					if (hdr.sample_rate < 1000) {
234 						int	x;
235 						char	y[10];
236 
237 						if (sscanf(val, " %lf %9s",
238 						    &x, y) != 1) {
239 							Err(
240 					MGET("ambiguous numeric option: %s\n"),
241 							    val);
242 							goto parse_error;
243 						}
244 					}
245 				} else if (hdr.ChannelParse(val)) {
246 					Err(MGET("invalid option value: %s\n"),
247 					    val);
248 					goto parse_error;
249 				}
250 			}
251 		}
252 	}
253 	free(buf);
254 	return (0);
255 
256 parse_error:
257 	free(buf);
258 	return (-1);
259 }
260