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