1 /*
2  *   libdi - CD Audio Device Interface Library
3  *
4  *   Copyright (C) 1993-2004  Ti Kan
5  *   E-mail: xmcd@amb.org
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License as published by
9  *   the Free Software Foundation; either version 2 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program 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
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 #ifndef lint
22 static char *_libdi_c_ident_ = "@(#)libdi.c	7.170 04/04/20";
23 #endif
24 
25 #include "common_d/appenv.h"
26 #include "common_d/version.h"
27 #include "common_d/util.h"
28 #include "libdi_d/libdi.h"
29 #include "libdi_d/scsipt.h"
30 #include "libdi_d/slioc.h"
31 #include "libdi_d/fbioc.h"
32 #include "libdi_d/aixioc.h"
33 #include "cdda_d/cdda.h"
34 
35 
36 #define PARM_BUF_SZ	(STR_BUF_SZ * 16)	/* Temporary buffer size */
37 
38 
39 extern appdata_t	app_data;
40 extern FILE		*errfp;
41 
42 
43 /* libdi module init routines */
44 diinit_tbl_t		diinit[] = {
45 	{ scsipt_init },		/* SCSI pass-through method */
46 	{ slioc_init },			/* SunOS/Solaris/Linux ioctl method */
47 	{ fbioc_init },			/* FreeBSD ioctl method */
48 	{ aixioc_init },		/* AIX IDE ioctl method */
49 	{ NULL }			/* List terminator */
50 };
51 
52 
53 /* libdi interface calling table */
54 di_tbl_t		ditbl[MAX_METHODS];
55 
56 /* Application callbacks */
57 di_client_t		*di_clinfo;
58 
59 /* Device list table */
60 char			**di_devlist;
61 
62 /* Clip frames */
63 sword32_t		di_clip_frames;
64 
65 /* Flag to indicate whether CD information has been loaded */
66 STATIC bool_t		di_cdinfo_loaded = FALSE;
67 
68 /* Device lock file */
69 STATIC char		lockfile[FILE_PATH_SZ] = { '\0' };
70 
71 
72 /***********************
73  *  internal routines  *
74  ***********************/
75 
76 
77 /*
78  * di_boolstr
79  *	Return the string "False" or "True" depending upon the
80  *	passed in boolean parameter.
81  *
82  * Args:
83  *	parm - boolean parameter
84  *
85  * Return:
86  *	"False" if parm is FALSE.
87  *	"True" is parm is TRUE.
88  */
89 STATIC char *
di_boolstr(bool_t parm)90 di_boolstr(bool_t parm)
91 {
92 	return ((parm == FALSE) ? "False" : "True");
93 }
94 
95 
96 /*
97  * di_prncfg
98  *	Display configuration information
99  *
100  * Args:
101  *	None.
102  *
103  * Return:
104  *	Nothing.
105  */
106 STATIC void
di_prncfg(void)107 di_prncfg(void)
108 {
109 	(void) fprintf(errfp, "\nBasic parameters:\n");
110 
111 	(void) fprintf(errfp, "\tlibdir:\t\t\t\t%s\n",
112 		       app_data.libdir);
113 
114 	(void) fprintf(errfp, "\nX resources:\n");
115 
116 	(void) fprintf(errfp, "\tversion:\t\t\t%s\n",
117 		       app_data.version == NULL ?
118 				"(undef)" : app_data.version);
119 	(void) fprintf(errfp, "\tmainWindowMode:\t\t\t%d\n",
120 		       app_data.main_mode);
121 	(void) fprintf(errfp, "\tmodeChangeGravity:\t\t%d\n",
122 		       app_data.modechg_grav);
123 	(void) fprintf(errfp, "\tnormalMainWidth:\t\t%d\n",
124 		       app_data.normal_width);
125 	(void) fprintf(errfp, "\tnormalMainHeight:\t\t%d\n",
126 		       app_data.normal_height);
127 	(void) fprintf(errfp, "\tbasicMainWidth:\t\t\t%d\n",
128 		       app_data.basic_width);
129 	(void) fprintf(errfp, "\tbasicMainHeight:\t\t%d\n",
130 		       app_data.basic_height);
131 	(void) fprintf(errfp, "\tdisplayBlinkOnInterval:\t\t%d\n",
132 		       app_data.blinkon_interval);
133 	(void) fprintf(errfp, "\tdisplayBlinkOffInterval:\t%d\n",
134 		       app_data.blinkoff_interval);
135 	(void) fprintf(errfp, "\tmainShowFocus:\t\t\t%s\n",
136 		       di_boolstr(app_data.main_showfocus));
137 	(void) fprintf(errfp, "\tinstallColormap:\t\t%s\n",
138 		       di_boolstr(app_data.instcmap));
139 
140 	(void) fprintf(errfp, "\nCommon parameters:\n");
141 
142 	(void) fprintf(errfp, "\tdevice:\t\t\t\t%s\n",
143 		       app_data.device);
144 	(void) fprintf(errfp, "\toutputPort:\t\t\t0x%x\n",
145 		       (int) app_data.outport);
146 	(void) fprintf(errfp, "\tcdinfoPath:\t\t\t%s\n",
147 		       app_data.cdinfo_path);
148 	(void) fprintf(errfp, "\tcharsetConvMode:\t\t%d\n",
149 		       app_data.chset_xlat);
150 	(void) fprintf(errfp, "\tlangUtf8:\t\t\t%s\n",
151 		       app_data.lang_utf8);
152 	(void) fprintf(errfp, "\tacceptFuzzyDefault:\t\t%s\n",
153 		       di_boolstr(app_data.single_fuzzy));
154 	(void) fprintf(errfp, "\tcdinfoFileMode:\t\t\t%s\n",
155 		       app_data.cdinfo_filemode);
156 	(void) fprintf(errfp, "\tproxyServer:\t\t\t%s\n",
157 		       app_data.proxy_server == NULL ?
158 		           "(undef)" : app_data.proxy_server);
159 	(void) fprintf(errfp, "\thistoryFileMode:\t\t%s\n",
160 		       app_data.hist_filemode);
161 	(void) fprintf(errfp, "\thistoryFileDisable:\t\t%s\n",
162 		       di_boolstr(app_data.histfile_dsbl));
163 	(void) fprintf(errfp, "\tcddbCacheTimeout:\t\t%d\n",
164 		       app_data.cache_timeout);
165 	(void) fprintf(errfp, "\tserviceTimeout:\t\t\t%d\n",
166 		       app_data.srv_timeout);
167 	(void) fprintf(errfp, "\tlocalDiscographyMode:\t\t%d\n",
168 		       app_data.discog_mode);
169 	(void) fprintf(errfp, "\tmaximumHistory:\t\t\t%d\n",
170 		       app_data.cdinfo_maxhist);
171 	(void) fprintf(errfp, "\tstatusPollInterval:\t\t%d\n",
172 		       app_data.stat_interval);
173 	(void) fprintf(errfp, "\tinsertPollInterval:\t\t%d\n",
174 		       app_data.ins_interval);
175 	(void) fprintf(errfp, "\ttimeDisplayMode:\t\t%d\n",
176 		       app_data.timedpy_mode);
177 	(void) fprintf(errfp, "\ttooltipDelayInterval:\t\t%d\n",
178 		       app_data.tooltip_delay);
179 	(void) fprintf(errfp, "\ttooltipActiveInterval:\t\t%d\n",
180 		       app_data.tooltip_time);
181 	(void) fprintf(errfp, "\tinsertPollDisable:\t\t%s\n",
182 		       di_boolstr(app_data.ins_disable));
183 	(void) fprintf(errfp, "\tpreviousThreshold:\t\t%d\n",
184 		       app_data.prev_threshold);
185 	(void) fprintf(errfp, "\tsearchSkipBlocks:\t\t%d\n",
186 		       app_data.skip_blks);
187 	(void) fprintf(errfp, "\tsearchPauseInterval:\t\t%d\n",
188 		       app_data.skip_pause);
189 	(void) fprintf(errfp, "\tsearchSpeedUpCount:\t\t%d\n",
190 		       app_data.skip_spdup);
191 	(void) fprintf(errfp, "\tsearchVolumePercent:\t\t%d\n",
192 		       app_data.skip_vol);
193 	(void) fprintf(errfp, "\tsearchMinVolume:\t\t%d\n",
194 		       app_data.skip_minvol);
195 	(void) fprintf(errfp, "\tsampleBlocks:\t\t\t%d\n",
196 		       app_data.sample_blks);
197 	(void) fprintf(errfp, "\tinternetOffline:\t\t%s\n",
198 		       di_boolstr(app_data.cdinfo_inetoffln));
199 	(void) fprintf(errfp, "\tcddbUseProxy:\t\t\t%s\n",
200 		       di_boolstr(app_data.use_proxy));
201 	(void) fprintf(errfp, "\tproxyAuthorization:\t\t%s\n",
202 		       di_boolstr(app_data.proxy_auth));
203 	(void) fprintf(errfp, "\tautoMusicBrowser:\t\t%s\n",
204 		       di_boolstr(app_data.auto_musicbrowser));
205 	(void) fprintf(errfp, "\tshowScsiErrMsg:\t\t\t%s\n",
206 		       di_boolstr(app_data.scsierr_msg));
207 	(void) fprintf(errfp, "\tsolaris2VolumeManager:\t\t%s\n",
208 		       di_boolstr(app_data.sol2_volmgt));
209 	(void) fprintf(errfp, "\ttooltipEnable:\t\t\t%s\n",
210 		       di_boolstr(app_data.tooltip_enable));
211 	(void) fprintf(errfp, "\tremoteControlEnable:\t\t%s\n",
212 		       di_boolstr(app_data.remote_enb));
213 	(void) fprintf(errfp, "\tremoteControlLog:\t\t%s\n",
214 		       di_boolstr(app_data.remote_log));
215 	(void) fprintf(errfp, "\tcddaFilePerTrack:\t\t%s\n",
216 		       di_boolstr(app_data.cdda_trkfile));
217 	(void) fprintf(errfp, "\tcddaSpaceToUnderscore:\t\t%s\n",
218 		       di_boolstr(app_data.subst_underscore));
219 	(void) fprintf(errfp, "\tcddaFileFormat:\t\t\t%d\n",
220 		       app_data.cdda_filefmt);
221 	(void) fprintf(errfp, "\tcddaFileTemplate:\t\t%s\n",
222 		       app_data.cdda_tmpl == NULL ? "-" : app_data.cdda_tmpl);
223 	(void) fprintf(errfp, "\tcddaPipeProgram:\t\t%s\n",
224 		       app_data.pipeprog == NULL ? "-" : app_data.pipeprog);
225 	(void) fprintf(errfp, "\tcddaSchedOptions:\t\t%d\n",
226 		       app_data.cdda_sched);
227 	(void) fprintf(errfp, "\tcddaHeartbeatTimeout:\t\t%d\n",
228 		       app_data.hb_timeout);
229 	(void) fprintf(errfp, "\tcompressionMode:\t\t%d\n",
230 		       app_data.comp_mode);
231 	(void) fprintf(errfp, "\tcompressionBitrate:\t\t%d\n",
232 		       app_data.bitrate);
233 	(void) fprintf(errfp, "\tminimumBitrate:\t\t\t%d\n",
234 		       app_data.bitrate_min);
235 	(void) fprintf(errfp, "\tmaximumBitrate:\t\t\t%d\n",
236 		       app_data.bitrate_max);
237 	(void) fprintf(errfp, "\tcompressionQuality:\t\t%d\n",
238 		       app_data.qual_factor);
239 	(void) fprintf(errfp, "\tchannelMode:\t\t\t%d\n",
240 		       app_data.chan_mode);
241 	(void) fprintf(errfp, "\tcompressionAlgorithm:\t\t%d\n",
242 		       app_data.comp_algo);
243 	(void) fprintf(errfp, "\tlowpassMode:\t\t\t%d\n",
244 		       app_data.lowpass_mode);
245 	(void) fprintf(errfp, "\tlowpassFrequency:\t\t%d\n",
246 		       app_data.lowpass_freq);
247 	(void) fprintf(errfp, "\tlowpassWidth:\t\t\t%d\n",
248 		       app_data.lowpass_width);
249 	(void) fprintf(errfp, "\thighpassMode:\t\t\t%d\n",
250 		       app_data.highpass_mode);
251 	(void) fprintf(errfp, "\thighpassFrequency:\t\t%d\n",
252 		       app_data.highpass_freq);
253 	(void) fprintf(errfp, "\thighpassWidth:\t\t\t%d\n",
254 		       app_data.highpass_width);
255 	(void) fprintf(errfp, "\tcopyrightFlag:\t\t\t%s\n",
256 		       di_boolstr(app_data.copyright));
257 	(void) fprintf(errfp, "\toriginalFlag:\t\t\t%s\n",
258 		       di_boolstr(app_data.original));
259 	(void) fprintf(errfp, "\tnoBitReservoirFlag:\t\t%s\n",
260 		       di_boolstr(app_data.nores));
261 	(void) fprintf(errfp, "\tchecksumFlag:\t\t\t%s\n",
262 		       di_boolstr(app_data.checksum));
263 	(void) fprintf(errfp, "\tstrictISO:\t\t\t%s\n",
264 		       di_boolstr(app_data.strict_iso));
265 	(void) fprintf(errfp, "\taddInfoTag:\t\t\t%s\n",
266 		       di_boolstr(app_data.add_tag));
267 	(void) fprintf(errfp, "\tlameOptionsMode:\t\t%d\n",
268 		       app_data.lameopts_mode);
269 	(void) fprintf(errfp, "\tlameOptions:\t\t\t%s\n",
270 		       app_data.lame_opts == NULL ? "-" : app_data.lame_opts);
271 	(void) fprintf(errfp, "\tid3TagMode:\t\t\t%d\n",
272 		       app_data.id3tag_mode);
273 	(void) fprintf(errfp, "\tspinDownOnLoad:\t\t\t%s\n",
274 		       di_boolstr(app_data.load_spindown));
275 	(void) fprintf(errfp, "\tplayOnLoad:\t\t\t%s\n",
276 		       di_boolstr(app_data.load_play));
277 	(void) fprintf(errfp, "\tejectOnDone:\t\t\t%s\n",
278 		       di_boolstr(app_data.done_eject));
279 	(void) fprintf(errfp, "\texitOnExit:\t\t\t%s\n",
280 		       di_boolstr(app_data.done_exit));
281 	(void) fprintf(errfp, "\tejectOnExit:\t\t\t%s\n",
282 		       di_boolstr(app_data.exit_eject));
283 	(void) fprintf(errfp, "\tstopOnExit:\t\t\t%s\n",
284 		       di_boolstr(app_data.exit_stop));
285 	(void) fprintf(errfp, "\texitOnEject:\t\t\t%s\n",
286 		       di_boolstr(app_data.eject_exit));
287 	(void) fprintf(errfp, "\trepeatMode:\t\t\t%s\n",
288 		       di_boolstr(app_data.repeat_mode));
289 	(void) fprintf(errfp, "\tshuffleMode:\t\t\t%s\n",
290 		       di_boolstr(app_data.shuffle_mode));
291 	(void) fprintf(errfp, "\tdiscogURLPrefix:\t\t%s\n",
292 		       app_data.discog_url_pfx == NULL ?
293 			   "-" : app_data.discog_url_pfx);
294 	(void) fprintf(errfp, "\tautoMotdDisable:\t\t%s\n",
295 		       di_boolstr(app_data.automotd_dsbl));
296 	(void) fprintf(errfp, "\tdebugLevel:\t\t\t0x%x\n",
297 		       (int) app_data.debug);
298 	(void) fprintf(errfp, "\texcludeWords:\t\t\t%s\n",
299 		       app_data.exclude_words == NULL ?
300 		           "(undef)" : app_data.exclude_words);
301 
302 	(void) fprintf(errfp, "\nDevice-specific (privileged) parameters:\n");
303 
304 	(void) fprintf(errfp, "\tdevnum:\t\t\t\t%d\n",
305 		       app_data.devnum);
306 	(void) fprintf(errfp, "\tdeviceList:\t\t\t%s\n",
307 		       app_data.devlist);
308 	(void) fprintf(errfp, "\tdeviceInterfaceMethod:\t\t%d\n",
309 		       app_data.di_method);
310 	(void) fprintf(errfp, "\tcddaMethod:\t\t\t%d\n",
311 		       app_data.cdda_method);
312 	(void) fprintf(errfp, "\tcddaReadMethod:\t\t\t%d\n",
313 		       app_data.cdda_rdmethod);
314 	(void) fprintf(errfp, "\tcddaWriteMethod:\t\t%d\n",
315 		       app_data.cdda_wrmethod);
316 	(void) fprintf(errfp, "\tcddaScsiModeSelect:\t\t%s\n",
317 		       di_boolstr(app_data.cdda_modesel));
318 	(void) fprintf(errfp, "\tcddaScsiDensity:\t\t%d\n",
319 		       app_data.cdda_scsidensity);
320 	(void) fprintf(errfp, "\tcddaScsiReadCommand:\t\t%d\n",
321 		       app_data.cdda_scsireadcmd);
322 	(void) fprintf(errfp, "\tcddaReadChunkBlocks:\t\t%d\n",
323 		       app_data.cdda_readchkblks);
324 	(void) fprintf(errfp, "\tcddaDataBigEndian:\t\t%s\n",
325 		       di_boolstr(app_data.cdda_bigendian));
326 	(void) fprintf(errfp, "\tdriveVendorCode:\t\t%d\n",
327 		       app_data.vendor_code);
328 	(void) fprintf(errfp, "\tscsiVersionCheck:\t\t%s\n",
329 		       di_boolstr(app_data.scsiverck));
330 	(void) fprintf(errfp, "\tnumDiscs:\t\t\t%d\n",
331 		       app_data.numdiscs);
332 	(void) fprintf(errfp, "\tmediumChangeMethod:\t\t%d\n",
333 		       app_data.chg_method);
334 	(void) fprintf(errfp, "\tscsiAudioVolumeBase:\t\t%d\n",
335 		       app_data.base_scsivol);
336 	(void) fprintf(errfp, "\tminimumPlayBlocks:\t\t%d\n",
337 		       app_data.min_playblks);
338 	(void) fprintf(errfp, "\tplayAudio10Support:\t\t%s\n",
339 		       di_boolstr(app_data.play10_supp));
340 	(void) fprintf(errfp, "\tplayAudio12Support:\t\t%s\n",
341 		       di_boolstr(app_data.play12_supp));
342 	(void) fprintf(errfp, "\tplayAudioMSFSupport:\t\t%s\n",
343 		       di_boolstr(app_data.playmsf_supp));
344 	(void) fprintf(errfp, "\tplayAudioTISupport:\t\t%s\n",
345 		       di_boolstr(app_data.playti_supp));
346 	(void) fprintf(errfp, "\tloadSupport:\t\t\t%s\n",
347 		       di_boolstr(app_data.load_supp));
348 	(void) fprintf(errfp, "\tejectSupport:\t\t\t%s\n",
349 		       di_boolstr(app_data.eject_supp));
350 	(void) fprintf(errfp, "\tmodeSenseSetDBD:\t\t%s\n",
351 		       di_boolstr(app_data.msen_dbd));
352 	(void) fprintf(errfp, "\tmodeSenseUse10Byte:\t\t%s\n",
353 		       di_boolstr(app_data.msen_10));
354 	(void) fprintf(errfp, "\tvolumeControlSupport:\t\t%s\n",
355 		       di_boolstr(app_data.mselvol_supp));
356 	(void) fprintf(errfp, "\tbalanceControlSupport:\t\t%s\n",
357 		       di_boolstr(app_data.balance_supp));
358 	(void) fprintf(errfp, "\tchannelRouteSupport:\t\t%s\n",
359 		       di_boolstr(app_data.chroute_supp));
360 	(void) fprintf(errfp, "\tpauseResumeSupport:\t\t%s\n",
361 		       di_boolstr(app_data.pause_supp));
362 	(void) fprintf(errfp, "\tstrictPauseResume:\t\t%s\n",
363 		       di_boolstr(app_data.strict_pause_resume));
364 	(void) fprintf(errfp, "\tplayPausePlay:\t\t\t%s\n",
365 		       di_boolstr(app_data.play_pause_play));
366 	(void) fprintf(errfp, "\tcaddyLockSupport:\t\t%s\n",
367 		       di_boolstr(app_data.caddylock_supp));
368 	(void) fprintf(errfp, "\tcurposFormat:\t\t\t%s\n",
369 		       di_boolstr(app_data.curpos_fmt));
370 	(void) fprintf(errfp, "\tnoTURWhenPlaying:\t\t%s\n",
371 		       di_boolstr(app_data.play_notur));
372 	(void) fprintf(errfp, "\ttocLBA:\t\t\t\t%s\n",
373 		       di_boolstr(app_data.toc_lba));
374 	(void) fprintf(errfp, "\tsubChannelLBA:\t\t\t%s\n",
375 		       di_boolstr(app_data.subq_lba));
376 	(void) fprintf(errfp, "\tdriveBlockSize:\t\t\t%d\n",
377 		       app_data.drv_blksz);
378 	(void) fprintf(errfp, "\tspinUpInterval:\t\t\t%d\n",
379 		       app_data.spinup_interval);
380 	(void) fprintf(errfp, "\tmcnDisable:\t\t\t%s\n",
381 		       di_boolstr(app_data.mcn_dsbl));
382 	(void) fprintf(errfp, "\tisrcDisable:\t\t\t%s\n",
383 		       di_boolstr(app_data.isrc_dsbl));
384 	(void) fprintf(errfp, "\tcdTextDisable:\t\t\t%s\n",
385 		       di_boolstr(app_data.cdtext_dsbl));
386 
387 	(void) fprintf(errfp,
388 		       "\nDevice-specific (user-modifiable) parameters:\n");
389 
390 	(void) fprintf(errfp, "\tplayMode:\t\t\t%d\n",
391 		       app_data.play_mode);
392 	(void) fprintf(errfp, "\tvolumeControlTaper:\t\t%d\n",
393 		       app_data.vol_taper);
394 	(void) fprintf(errfp, "\tstartupVolume:\t\t\t%d\n",
395 		       app_data.startup_vol);
396 	(void) fprintf(errfp, "\tchannelRoute:\t\t\t%d\n",
397 		       app_data.ch_route);
398 	(void) fprintf(errfp, "\tcloseOnEject:\t\t\t%s\n",
399 		       di_boolstr(app_data.eject_close));
400 	(void) fprintf(errfp, "\tcaddyLock:\t\t\t%s\n",
401 		       di_boolstr(app_data.caddy_lock));
402 	(void) fprintf(errfp, "\tmultiPlay:\t\t\t%s\n",
403 		       di_boolstr(app_data.multi_play));
404 	(void) fprintf(errfp, "\treversePlay:\t\t\t%s\n",
405 		       di_boolstr(app_data.reverse));
406 	(void) fprintf(errfp, "\tcddaJitterCorrection:\t\t%s\n",
407 		       di_boolstr(app_data.cdda_jitter_corr));
408 
409 	(void) fprintf(errfp, "\n");
410 }
411 
412 
413 /*
414  * di_parse_devlist
415  *	Parse the app_data.devlist string and create the di_devlist array.
416  *
417  * Args:
418  *	None.
419  *
420  * Return:
421  *	Nothing.
422  */
423 STATIC void
di_parse_devlist(void)424 di_parse_devlist(void)
425 {
426 	char		*p,
427 			*q;
428 	int		i,
429 			n,
430 			listsz;
431 	curstat_t	*s = di_clinfo->curstat_addr();
432 
433 	if (app_data.chg_method < 0 || app_data.chg_method >= MAX_CHG_METHODS)
434 		/* Fix-up in case of mis-configuration */
435 		app_data.chg_method = CHG_NONE;
436 
437 	n = app_data.numdiscs;
438 
439 	switch (app_data.chg_method) {
440 	case CHG_SCSI_MEDCHG:
441 		n = 2;
442 		/*FALLTHROUGH*/
443 
444 	case CHG_SCSI_LUN:
445 		/* SCSI LUN addressing method */
446 		listsz = n * sizeof(char *);
447 
448 		di_devlist = (char **) MEM_ALLOC("di_devlist", listsz);
449 		if (di_devlist == NULL) {
450 			DI_FATAL(app_data.str_nomemory);
451 			return;
452 		}
453 		(void) memset(di_devlist, 0, listsz);
454 
455 		p = q = app_data.devlist;
456 		if (p == NULL || *p == '\0') {
457 			DI_FATAL(app_data.str_devlist_undef);
458 			return;
459 		}
460 
461 		for (i = 0; i < n; i++) {
462 			q = strchr(p, ';');
463 
464 			if (q == NULL && i < (n - 1)) {
465 				DI_FATAL(app_data.str_devlist_count);
466 				return;
467 			}
468 
469 			if (q != NULL)
470 				*q = '\0';
471 
472 			if (!util_newstr(&di_devlist[i], p)) {
473 				DI_FATAL(app_data.str_nomemory);
474 				return;
475 			}
476 
477 			if (q != NULL)
478 				*q = ';';
479 
480 			p = q + 1;
481 		}
482 
483 		/* In this mode, closeOnEject must be True */
484 		app_data.eject_close = TRUE;
485 		break;
486 
487 	case CHG_OS_IOCTL:
488 	case CHG_NONE:
489 	default:
490 		if (app_data.chg_method == CHG_OS_IOCTL) {
491 			/* In this mode, closeOnEject must be True */
492 			app_data.eject_close = TRUE;
493 		}
494 		else {
495 			/* Some fix-ups in case of mis-configuration */
496 			app_data.numdiscs = 1;
497 		}
498 
499 		if (app_data.devlist == NULL) {
500 			if (!util_newstr(&app_data.devlist, app_data.device)) {
501 				DI_FATAL(app_data.str_nomemory);
502 				return;
503 			}
504 		}
505 		else if (strcmp(app_data.devlist, app_data.device) != 0) {
506 			MEM_FREE(app_data.devlist);
507 			app_data.devlist = NULL;
508 			if (!util_newstr(&app_data.devlist, app_data.device)) {
509 				DI_FATAL(app_data.str_nomemory);
510 				return;
511 			}
512 		}
513 
514 		di_devlist = (char **) MEM_ALLOC("di_devlist", sizeof(char *));
515 		if (di_devlist == NULL) {
516 			DI_FATAL(app_data.str_nomemory);
517 			return;
518 		}
519 		di_devlist[0] = NULL;
520 		if (!util_newstr(&di_devlist[0], app_data.devlist)) {
521 			DI_FATAL(app_data.str_nomemory);
522 			return;
523 		}
524 		break;
525 	}
526 
527 	/* Initialize to the first device */
528 	s->curdev = di_devlist[0];
529 }
530 
531 
532 /*
533  * di_chktmpl
534  *	Check the CDDA save-to-file path template for the specified token.
535  *
536  * Args:
537  *	s - Pointer to the curstat_t structure
538  *	tok - The token string to look for
539  *
540  * Return:
541  *	TRUE  - Token is found
542  *	FALSE - Token not found
543  */
544 STATIC bool_t
di_chktmpl(curstat_t * s,char * tok)545 di_chktmpl(curstat_t *s, char *tok)
546 {
547 	char	*p;
548 
549 	if ((p = util_strstr(s->outf_tmpl, tok)) != NULL) {
550 		if (p == s->outf_tmpl) {
551 			/* The token is found at the beginning
552 			 * of the template string.
553 			 */
554 			return TRUE;
555 		}
556 		else if (p == (s->outf_tmpl + 1)) {
557 			if (*(p-1) != '%') {
558 				/* The token is found and is not a character
559 				 * after a "%%" token.  This is a special
560 				 * case check for cases where the token
561 				 * is 1 character offset into the template
562 				 * string.
563 				 */
564 				return TRUE;
565 			}
566 		}
567 		else if (*(p-1) != '%' || *(p-2) == '%') {
568 			/* The token is found and is not a character after
569 			 * a "%%" token.  This is a "general" check for
570 			 * tokens that appear at 2 character offset into
571 			 * the template string or more.
572 			 */
573 			return TRUE;
574 		}
575 	}
576 
577 	/* The token is not found in the template string */
578 	return FALSE;
579 }
580 
581 
582 /***********************
583  *   public routines   *
584  ***********************/
585 
586 
587 /*
588  * di_init
589  *	Top-level function to initialize the libdi modules.
590  *
591  * Args:
592  *	s - Pointer to the curstat_t structure
593  *
594  * Return:
595  *	Nothing.
596  */
597 void
di_init(di_client_t * clp)598 di_init(di_client_t *clp)
599 {
600 	int		i;
601 	curstat_t	*s = clp->curstat_addr();
602 
603 	di_clinfo = (di_client_t *)(void *) MEM_ALLOC(
604 		"di_client_t",
605 		sizeof(di_client_t)
606 	);
607 	if (di_clinfo == NULL) {
608 		DI_FATAL(app_data.str_nomemory);
609 		return;
610 	}
611 	(void) memcpy(di_clinfo, clp, sizeof(di_client_t));
612 
613 #if defined(DI_SCSIPT) && defined(DEMO_ONLY)
614 	/* Hardwire some params for demo mode.  This overrides the
615 	 * parameters from the config files.
616 	 */
617 	app_data.di_method = DI_SCSIPT;
618 	app_data.vendor_code = VENDOR_SCSI2;
619 	app_data.play10_supp = TRUE;
620 	app_data.play12_supp = TRUE;
621 	app_data.playmsf_supp = TRUE;
622 	app_data.playti_supp = TRUE;
623 	app_data.load_supp = TRUE;
624 	app_data.eject_supp = TRUE;
625 	app_data.msen_dbd = FALSE;
626 	app_data.mselvol_supp = TRUE;
627 	app_data.balance_supp = TRUE;
628 	app_data.chroute_supp = TRUE;
629 	app_data.pause_supp = TRUE;
630 	app_data.caddylock_supp = TRUE;
631 	app_data.curpos_fmt = TRUE;
632 	app_data.cdda_scsidensity = 0;
633 	app_data.cdda_scsireadcmd = 0;	/* MMC Read CD */
634 	app_data.cdda_readchkblks = 4;	/* Max xfer length of cdsim */
635 	app_data.cdda_method = 0;       /* None */
636 	app_data.cdda_rdmethod = 1;     /* SCSI pass-through */
637 	app_data.cdda_wrmethod = 8;     /* File and pipe only */
638 	app_data.cdda_jitter_corr = FALSE;
639 #else
640 	/* Sanity check */
641 	if (app_data.di_method < 0 || app_data.di_method >= MAX_METHODS) {
642 		DI_FATAL(app_data.str_nomethod);
643 		return;
644 	}
645 #endif
646 
647 	/* Parse the device list and set up structure */
648 	di_parse_devlist();
649 
650 	/* Check string lengths.  This is to avoid subsequent
651 	 * string buffer overflows.
652 	 */
653 	if (((int) (strlen(app_data.str_notrom) +
654 		    strlen(app_data.device)) >= ERR_BUF_SZ) ||
655 	    ((int) (strlen(app_data.str_notscsi2) +
656 		    strlen(app_data.device)) >= ERR_BUF_SZ) ||
657 	    ((int) (strlen(app_data.str_staterr) +
658 		    strlen(app_data.device)) >= ERR_BUF_SZ) ||
659 	    ((int) (strlen(app_data.str_noderr) +
660 		    strlen(app_data.device)) >= ERR_BUF_SZ) ||
661 	    ((int) (strlen(app_data.str_nocfg) + 12 +
662 		    strlen(app_data.libdir)) >= ERR_BUF_SZ)) {
663 		DI_FATAL(app_data.str_longpatherr);
664 		return;
665 	}
666 
667 	lockfile[0] = '\0';
668 
669 	/* Initialize the libdi modules */
670 	for (i = 0; i < MAX_METHODS; i++) {
671 		if (diinit[i].init != NULL)
672 			diinit[i].init(s, &ditbl[i]);
673 	}
674 
675 	/* Sanity check again */
676 	if (ditbl[app_data.di_method].methodstr == NULL) {
677 		DI_FATAL(app_data.str_nomethod);
678 		return;
679 	}
680 
681 	/* Fill in device capabilities bitmask return */
682 	clp->capab = (cdda_capab() | CAPAB_PLAYAUDIO);
683 }
684 
685 
686 /*
687  * di_bitrate_valid
688  *	Check if a bitrate is valid.
689  *
690  * Args:
691  *	bitrate - The bitrate in kb/s to check
692  *
693  * Returns:
694  *	TRUE  - valid
695  *	FALSE - invalid
696  */
697 bool_t
di_bitrate_valid(int bitrate)698 di_bitrate_valid(int bitrate)
699 {
700 	int	i,
701 		n = cdda_bitrates();
702 
703 	if (bitrate == 0)
704 		return TRUE;	/* Special case for "default" bitrate */
705 
706 	/* Check against list of valid bitrates */
707 	for (i = 0; i < n; i++) {
708 		int	br = cdda_bitrate_val(i);
709 
710 		if (br >= 32 && br == bitrate)
711 			break;
712 	}
713 	if (i == n)
714 		return FALSE;
715 
716 	return TRUE;
717 }
718 
719 
720 /*
721  * di_load_cdtext
722  *	Read CD-TEXT information from the CD if available, parse the raw
723  *	data and fill the di_cdtext_t structure.
724  *
725  * Args:
726  *	s - Pointer to the curstat_t structure
727  *	t - Pointer to the di_cdtext_t structure
728  *
729  * Return:
730  *	Nothing.
731  */
732 void
di_load_cdtext(curstat_t * s,di_cdtext_t * t)733 di_load_cdtext(curstat_t *s, di_cdtext_t *t)
734 {
735 	if (ditbl[app_data.di_method].load_cdtext != NULL)
736 		ditbl[app_data.di_method].load_cdtext(s, t);
737 	else
738 		t->cdtext_valid = FALSE;
739 }
740 
741 
742 /*
743  * di_clear_cdtext
744  *	Clear the contents of the CD-TEXT information structure.
745  *
746  * Args:
747  *	t - Pointer to the di_cdtext_t structure
748  *
749  * Return:
750  *	Nothing.
751  */
752 void
di_clear_cdtext(di_cdtext_t * t)753 di_clear_cdtext(di_cdtext_t *t)
754 {
755 	int	i;
756 
757 	t->cdtext_valid = FALSE;
758 	if (t->disc.title != NULL) {
759 		MEM_FREE(t->disc.title);
760 		t->disc.title = NULL;
761 	}
762 	if (t->disc.performer != NULL) {
763 		MEM_FREE(t->disc.performer);
764 		t->disc.performer = NULL;
765 	}
766 	if (t->disc.songwriter != NULL) {
767 		MEM_FREE(t->disc.songwriter);
768 		t->disc.songwriter = NULL;
769 	}
770 	if (t->disc.composer != NULL) {
771 		MEM_FREE(t->disc.composer);
772 		t->disc.composer = NULL;
773 	}
774 	if (t->disc.arranger != NULL) {
775 		MEM_FREE(t->disc.arranger);
776 		t->disc.arranger = NULL;
777 	}
778 	if (t->disc.message != NULL) {
779 		MEM_FREE(t->disc.message);
780 		t->disc.message = NULL;
781 	}
782 	if (t->disc.catno != NULL) {
783 		MEM_FREE(t->disc.catno);
784 		t->disc.catno = NULL;
785 	}
786 	for (i = 0; i < MAXTRACK; i++) {
787 		if (t->track[i].title != NULL) {
788 			MEM_FREE(t->track[i].title);
789 			t->track[i].title = NULL;
790 		}
791 		if (t->track[i].performer != NULL) {
792 			MEM_FREE(t->track[i].performer);
793 			t->track[i].performer = NULL;
794 		}
795 		if (t->track[i].songwriter != NULL) {
796 			MEM_FREE(t->track[i].songwriter);
797 			t->track[i].songwriter = NULL;
798 		}
799 		if (t->track[i].composer != NULL) {
800 			MEM_FREE(t->track[i].composer);
801 			t->track[i].composer = NULL;
802 		}
803 		if (t->track[i].arranger != NULL) {
804 			MEM_FREE(t->track[i].arranger);
805 			t->track[i].arranger = NULL;
806 		}
807 		if (t->track[i].message != NULL) {
808 			MEM_FREE(t->track[i].message);
809 			t->track[i].message = NULL;
810 		}
811 		if (t->track[i].catno != NULL) {
812 			MEM_FREE(t->track[i].catno);
813 			t->track[i].catno = NULL;
814 		}
815 	}
816 }
817 
818 
819 /*
820  * di_playmode
821  *	Init/halt the CDDA mode
822  *
823  * Args:
824  *	s - Pointer to the curstat_t structure
825  *
826  * Return:
827  *	TRUE - success
828  *	FALSE - failure
829  */
830 bool_t
di_playmode(curstat_t * s)831 di_playmode(curstat_t *s)
832 {
833 	switch (s->mode) {
834 	case MOD_STOP:
835 	case MOD_NODISC:
836 	case MOD_BUSY:
837 		break;
838 	default:
839 		/* Stop playback first before changing mode */
840 		di_stop(s, TRUE);
841 		break;
842 	}
843 
844 	/* Set clip frames */
845 	if (PLAYMODE_IS_STD(app_data.play_mode))
846 		di_clip_frames = CLIP_FRAMES;
847 	else
848 		di_clip_frames = 0;
849 
850 	if (ditbl[app_data.di_method].playmode != NULL)
851 		return (ditbl[app_data.di_method].playmode(s));
852 
853 	return FALSE;
854 }
855 
856 
857 /*
858  * di_check_disc
859  *	Check if disc is ready for use
860  *
861  * Args:
862  *	s - Pointer to the curstat_t structure
863  *
864  * Return:
865  *	TRUE - success
866  *	FALSE - failure
867  */
868 bool_t
di_check_disc(curstat_t * s)869 di_check_disc(curstat_t *s)
870 {
871 	if (ditbl[app_data.di_method].check_disc != NULL)
872 		return (ditbl[app_data.di_method].check_disc(s));
873 
874 	return FALSE;
875 }
876 
877 
878 /*
879  * di_status_upd
880  *	Force update of playback status
881  *
882  * Args:
883  *	s - Pointer to the curstat_t structure
884  *
885  * Return:
886  *	Nothing.
887  */
888 void
di_status_upd(curstat_t * s)889 di_status_upd(curstat_t *s)
890 {
891 	if (ditbl[app_data.di_method].status_upd != NULL)
892 		ditbl[app_data.di_method].status_upd(s);
893 }
894 
895 
896 /*
897  * di_lock
898  *	Caddy lock function
899  *
900  * Args:
901  *	s - Pointer to the curstat_t structure
902  *	enable - whether to enable/disable caddy lock
903  *
904  * Return:
905  *	Nothing.
906  */
907 void
di_lock(curstat_t * s,bool_t enable)908 di_lock(curstat_t *s, bool_t enable)
909 {
910 	if (ditbl[app_data.di_method].lock != NULL)
911 		ditbl[app_data.di_method].lock(s, enable);
912 }
913 
914 
915 /*
916  * di_repeat
917  *	Repeat mode function
918  *
919  * Args:
920  *	s - Pointer to the curstat_t structure
921  *	enable - whether to enable/disable repeat mode
922  *
923  * Return:
924  *	Nothing.
925  */
926 void
di_repeat(curstat_t * s,bool_t enable)927 di_repeat(curstat_t *s, bool_t enable)
928 {
929 	if (ditbl[app_data.di_method].repeat != NULL)
930 		ditbl[app_data.di_method].repeat(s, enable);
931 }
932 
933 
934 /*
935  * di_shuffle
936  *	Shuffle mode function
937  *
938  * Args:
939  *	s - Pointer to the curstat_t structure
940  *	enable - whether to enable/disable shuffle mode
941  *
942  * Return:
943  *	Nothing.
944  */
945 void
di_shuffle(curstat_t * s,bool_t enable)946 di_shuffle(curstat_t *s, bool_t enable)
947 {
948 	if (ditbl[app_data.di_method].shuffle != NULL)
949 		ditbl[app_data.di_method].shuffle(s, enable);
950 }
951 
952 
953 /*
954  * di_load_eject
955  *	CD caddy load and eject function.  If disc caddy is not
956  *	loaded, it will attempt to load it.  Otherwise, it will be
957  *	ejected.
958  *
959  * Args:
960  *	s - Pointer to the curstat_t structure
961  *
962  * Return:
963  *	Nothing.
964  */
965 void
di_load_eject(curstat_t * s)966 di_load_eject(curstat_t *s)
967 {
968 	if (ditbl[app_data.di_method].load_eject != NULL)
969 		ditbl[app_data.di_method].load_eject(s);
970 }
971 
972 
973 /*
974  * di_ab
975  *	A->B segment play mode function
976  *
977  * Args:
978  *	s - Pointer to the curstat_t structure
979  *
980  * Return:
981  *	Nothing.
982  */
983 void
di_ab(curstat_t * s)984 di_ab(curstat_t *s)
985 {
986 	if (ditbl[app_data.di_method].ab != NULL)
987 		ditbl[app_data.di_method].ab(s);
988 }
989 
990 
991 /*
992  * di_sample
993  *	Sample play mode function
994  *
995  * Args:
996  *	s - Pointer to the curstat_t structure
997  *
998  * Return:
999  *	Nothing.
1000  */
1001 void
di_sample(curstat_t * s)1002 di_sample(curstat_t *s)
1003 {
1004 	if (ditbl[app_data.di_method].sample != NULL)
1005 		ditbl[app_data.di_method].sample(s);
1006 }
1007 
1008 
1009 /*
1010  * di_level
1011  *	Audio volume control function
1012  *
1013  * Args:
1014  *	s - Pointer to the curstat_t structure
1015  *	level - The volume level to set to
1016  *	drag - Whether this is an update due to the user dragging the
1017  *		volume control slider thumb.  If this is FALSE, then
1018  *		a final volume setting has been found.
1019  *
1020  * Return:
1021  *	Nothing.
1022  */
1023 void
di_level(curstat_t * s,byte_t level,bool_t drag)1024 di_level(curstat_t *s, byte_t level, bool_t drag)
1025 {
1026 	if (ditbl[app_data.di_method].level != NULL)
1027 		ditbl[app_data.di_method].level(s, level, drag);
1028 }
1029 
1030 
1031 /*
1032  * di_play_pause
1033  *	Audio playback and pause function
1034  *
1035  * Args:
1036  *	s - Pointer to the curstat_t structure
1037  *
1038  * Return:
1039  *	Nothing.
1040  */
1041 void
di_play_pause(curstat_t * s)1042 di_play_pause(curstat_t *s)
1043 {
1044 	if (ditbl[app_data.di_method].play_pause != NULL)
1045 		ditbl[app_data.di_method].play_pause(s);
1046 }
1047 
1048 
1049 /*
1050  * di_stop
1051  *	Stop function
1052  *
1053  * Args:
1054  *	s - Pointer to the curstat_t structure
1055  *	stop_disc - Whether to actually spin down the disc or just
1056  *		update status.
1057  *
1058  * Return:
1059  *	Nothing.
1060  */
1061 void
di_stop(curstat_t * s,bool_t stop_disc)1062 di_stop(curstat_t *s, bool_t stop_disc)
1063 {
1064 	if (ditbl[app_data.di_method].stop != NULL)
1065 		ditbl[app_data.di_method].stop(s, stop_disc);
1066 }
1067 
1068 
1069 /*
1070  * di_chgdisc
1071  *	Change disc function
1072  *
1073  * Args:
1074  *	s - Pointer to the curstat_t structure
1075  *
1076  * Return:
1077  *	Nothing.
1078  */
1079 void
di_chgdisc(curstat_t * s)1080 di_chgdisc(curstat_t *s)
1081 {
1082 	if (ditbl[app_data.di_method].chgdisc != NULL)
1083 		ditbl[app_data.di_method].chgdisc(s);
1084 }
1085 
1086 
1087 /*
1088  * di_prevtrk
1089  *	Previous track function
1090  *
1091  * Args:
1092  *	s - Pointer to the curstat_t structure
1093  *
1094  * Return:
1095  *	Nothing.
1096  */
1097 void
di_prevtrk(curstat_t * s)1098 di_prevtrk(curstat_t *s)
1099 {
1100 	if (ditbl[app_data.di_method].prevtrk != NULL)
1101 		ditbl[app_data.di_method].prevtrk(s);
1102 }
1103 
1104 
1105 /*
1106  * di_nexttrk
1107  *	Next track function
1108  *
1109  * Args:
1110  *	s - Pointer to the curstat_t structure
1111  *
1112  * Return:
1113  *	Nothing.
1114  */
1115 void
di_nexttrk(curstat_t * s)1116 di_nexttrk(curstat_t *s)
1117 {
1118 	if (ditbl[app_data.di_method].nexttrk != NULL)
1119 		ditbl[app_data.di_method].nexttrk(s);
1120 }
1121 
1122 
1123 /*
1124  * di_previdx
1125  *	Previous index function
1126  *
1127  * Args:
1128  *	s - Pointer to the curstat_t structure
1129  *
1130  * Return:
1131  *	Nothing.
1132  */
1133 void
di_previdx(curstat_t * s)1134 di_previdx(curstat_t *s)
1135 {
1136 	if (ditbl[app_data.di_method].previdx != NULL)
1137 		ditbl[app_data.di_method].previdx(s);
1138 }
1139 
1140 
1141 /*
1142  * di_nextidx
1143  *	Next index function
1144  *
1145  * Args:
1146  *	s - Pointer to the curstat_t structure
1147  *
1148  * Return:
1149  *	Nothing.
1150  */
1151 void
di_nextidx(curstat_t * s)1152 di_nextidx(curstat_t *s)
1153 {
1154 	if (ditbl[app_data.di_method].nextidx != NULL)
1155 		ditbl[app_data.di_method].nextidx(s);
1156 }
1157 
1158 
1159 /*
1160  * di_rew
1161  *	Search-rewind function
1162  *
1163  * Args:
1164  *	s - Pointer to the curstat_t structure
1165  *
1166  * Return:
1167  *	Nothing.
1168  */
1169 void
di_rew(curstat_t * s,bool_t start)1170 di_rew(curstat_t *s, bool_t start)
1171 {
1172 	if (ditbl[app_data.di_method].rew != NULL)
1173 		ditbl[app_data.di_method].rew(s, start);
1174 }
1175 
1176 
1177 /*
1178  * di_ff
1179  *	Search-fast-forward function
1180  *
1181  * Args:
1182  *	s - Pointer to the curstat_t structure
1183  *
1184  * Return:
1185  *	Nothing.
1186  */
1187 void
di_ff(curstat_t * s,bool_t start)1188 di_ff(curstat_t *s, bool_t start)
1189 {
1190 	if (ditbl[app_data.di_method].ff != NULL)
1191 		ditbl[app_data.di_method].ff(s, start);
1192 }
1193 
1194 
1195 /*
1196  * di_warp
1197  *	Track warp function
1198  *
1199  * Args:
1200  *	s - Pointer to the curstat_t structure
1201  *
1202  * Return:
1203  *	Nothing.
1204  */
1205 void
di_warp(curstat_t * s)1206 di_warp(curstat_t *s)
1207 {
1208 	if (ditbl[app_data.di_method].warp != NULL)
1209 		ditbl[app_data.di_method].warp(s);
1210 }
1211 
1212 
1213 /*
1214  * di_route
1215  *	Channel routing function
1216  *
1217  * Args:
1218  *	s - Pointer to the curstat_t structure
1219  *
1220  * Return:
1221  *	Nothing.
1222  */
1223 void
di_route(curstat_t * s)1224 di_route(curstat_t *s)
1225 {
1226 	if (ditbl[app_data.di_method].route != NULL)
1227 		ditbl[app_data.di_method].route(s);
1228 }
1229 
1230 
1231 /*
1232  * di_mute_on
1233  *	Mute audio function
1234  *
1235  * Args:
1236  *	s - Pointer to the curstat_t structure
1237  *
1238  * Return:
1239  *	Nothing.
1240  */
1241 void
di_mute_on(curstat_t * s)1242 di_mute_on(curstat_t *s)
1243 {
1244 	if (ditbl[app_data.di_method].mute_on != NULL)
1245 		ditbl[app_data.di_method].mute_on(s);
1246 }
1247 
1248 
1249 /*
1250  * di_mute_off
1251  *	Un-mute audio function
1252  *
1253  * Args:
1254  *	s - Pointer to the curstat_t structure
1255  *
1256  * Return:
1257  *	Nothing.
1258  */
1259 void
di_mute_off(curstat_t * s)1260 di_mute_off(curstat_t *s)
1261 {
1262 	if (ditbl[app_data.di_method].mute_off != NULL)
1263 		ditbl[app_data.di_method].mute_off(s);
1264 }
1265 
1266 
1267 /*
1268  * di_cddajitter
1269  *	CDDA jitter correction setting change notification function
1270  *
1271  * Args:
1272  *	s - Pointer to the curstat_t structure
1273  *
1274  * Return:
1275  *	Nothing.
1276  */
1277 void
di_cddajitter(curstat_t * s)1278 di_cddajitter(curstat_t *s)
1279 {
1280 	if (ditbl[app_data.di_method].cddajitter != NULL)
1281 		ditbl[app_data.di_method].cddajitter(s);
1282 }
1283 
1284 
1285 /*
1286  * di_debug
1287  *	Debug level change notification function
1288  *
1289  * Args:
1290  *	None.
1291  *
1292  * Return:
1293  *	Nothing.
1294  */
1295 void
di_debug(void)1296 di_debug(void)
1297 {
1298 	if (ditbl[app_data.di_method].debug != NULL)
1299 		ditbl[app_data.di_method].debug();
1300 }
1301 
1302 
1303 /*
1304  * di_start
1305  *	Start the SCSI pass-through module.
1306  *
1307  * Args:
1308  *	s - Pointer to the curstat_t structure
1309  *
1310  * Return:
1311  *	Nothing.
1312  */
1313 void
di_start(curstat_t * s)1314 di_start(curstat_t *s)
1315 {
1316 	if (app_data.debug & DBG_ALL)
1317 		di_prncfg();	/* Print debug information */
1318 
1319 	if (ditbl[app_data.di_method].start != NULL)
1320 		ditbl[app_data.di_method].start(s);
1321 }
1322 
1323 
1324 /*
1325  * di_icon
1326  *	Handler for main window iconification/de-iconification
1327  *
1328  * Args:
1329  *	s - Pointer to the curstat_t structure
1330  *	iconified - Whether the main window is iconified
1331  *
1332  * Return:
1333  *	Nothing.
1334  */
1335 void
di_icon(curstat_t * s,bool_t iconified)1336 di_icon(curstat_t *s, bool_t iconified)
1337 {
1338 	if (ditbl[app_data.di_method].icon != NULL)
1339 		ditbl[app_data.di_method].icon(s, iconified);
1340 }
1341 
1342 
1343 /*
1344  * di_halt
1345  *	Shut down the SCSI pass-through and vendor-unique modules.
1346  *
1347  * Args:
1348  *	s - Pointer to the curstat_t structure
1349  *
1350  * Return:
1351  *	Nothing.
1352  */
1353 void
di_halt(curstat_t * s)1354 di_halt(curstat_t *s)
1355 {
1356 	if (ditbl[app_data.di_method].halt != NULL)
1357 		ditbl[app_data.di_method].halt(s);
1358 
1359 	di_devunlock(s);
1360 }
1361 
1362 
1363 /*
1364  * di_dump_curstat
1365  *	Display contents of the the curstat_t structure.
1366  *
1367  * Args:
1368  *	s - Pointer to the curstat_t structure.
1369  *
1370  * Return:
1371  *	Nothing.
1372  */
1373 void
di_dump_curstat(curstat_t * s)1374 di_dump_curstat(curstat_t *s)
1375 {
1376 	int		i;
1377 
1378 	(void) fprintf(errfp,
1379 		       "\nDumping contents of curstat_t structure at 0x%lx:\n",
1380 		       (unsigned long) s);
1381 	(void) fprintf(errfp,
1382 		       "curdev=%s\nfirst_disc=%d last_disc=%d\n",
1383 		       s->curdev, s->first_disc, s->last_disc);
1384 	(void) fprintf(errfp,
1385 		       "cur_disc=%d prev_disc=%d\n",
1386 		       s->cur_disc, s->prev_disc);
1387 	(void) fprintf(errfp,
1388 		       "mode=%d time_dpy=%d flags=0x%x\n",
1389 		       s->mode, s->time_dpy, s->flags);
1390 	(void) fprintf(errfp,
1391 		       "first_trk=%d last_trk=%d tot_trks=%d mcn=\"%s\"\n",
1392 		       s->first_trk, s->last_trk, s->tot_trks, s->mcn);
1393 	(void) fprintf(errfp, "cur_trk=%d cur_idx=%d\n",
1394 		       s->cur_trk, s->cur_idx);
1395 	(void) fprintf(errfp,
1396 		      "discpos_tot.msf=%02u:%02u:%02u discpos_tot.addr=0x%x\n",
1397 		       s->discpos_tot.min, s->discpos_tot.sec,
1398 		       s->discpos_tot.frame, s->discpos_tot.addr);
1399 	(void) fprintf(errfp,
1400 		       "curpos_tot.msf=%02u:%02u:%02u curpos_tot.addr=0x%x\n",
1401 		       s->curpos_tot.min, s->curpos_tot.sec,
1402 		       s->curpos_tot.frame, s->curpos_tot.addr);
1403 	(void) fprintf(errfp,
1404 		       "curpos_trk.msf=%02u:%02u:%02u curpos_trk.addr=0x%x\n",
1405 		       s->curpos_tot.min, s->curpos_tot.sec,
1406 		       s->curpos_tot.frame, s->curpos_tot.addr);
1407 
1408 	for (i = 0; i < MAXTRACK; i++) {
1409 		(void) fprintf(errfp,
1410 			       "[%2d] trkno=%03d addr=%06d (0x%05x) "
1411 			       "msf=%02d:%02d:%02d type=%d isrc=\"%s\"\n",
1412 			       i, s->trkinfo[i].trkno,
1413 			       s->trkinfo[i].addr, s->trkinfo[i].addr,
1414 			       s->trkinfo[i].min, s->trkinfo[i].sec,
1415 			       s->trkinfo[i].frame, s->trkinfo[i].type,
1416 			       s->trkinfo[i].isrc);
1417 		if (s->trkinfo[i].trkno == LEAD_OUT_TRACK)
1418 			break;
1419 	}
1420 
1421 	(void) fprintf(errfp, "playorder=");
1422 	for (i = 0; i < (int) s->prog_tot; i++)
1423 		(void) fprintf(errfp, "%d ",
1424 			s->trkinfo[s->trkinfo[i].playorder].trkno);
1425 	(void) fprintf(errfp, "\n");
1426 
1427 	(void) fprintf(errfp,
1428 		"sav_iaddr=0x%x rptcnt=%d repeat=%d shuffle=%d program=%d\n",
1429 		s->sav_iaddr, s->rptcnt, s->repeat, s->shuffle, s->program);
1430 	(void) fprintf(errfp,
1431 		"onetrk_prog=%d caddy_lk=%d prog_tot=%d prog_cnt=%d\n",
1432 		s->onetrk_prog, s->caddy_lock,
1433 		s->prog_tot, s->prog_cnt);
1434 	(void) fprintf(errfp,
1435 		"chgrscan=%d level=%d "
1436 		"level_left=%d level_right=%d cdda_att=%d\n",
1437 		s->chgrscan, s->level, s->level_left, s->level_right,
1438 		s->cdda_att);
1439 	(void) fprintf(errfp,
1440 		"vendor=\"%-8s\" prod=\"%-16s\" revnum=\"%-4s\"\n",
1441 		s->vendor, s->prod, s->revnum);
1442 	(void) fprintf(errfp, "outf_tmpl=%s\n",
1443 		s->outf_tmpl == NULL ? "NULL" : s->outf_tmpl);
1444 }
1445 
1446 
1447 /*
1448  * di_common_parmload
1449  *	Load the common configuration file and initialize parameters.
1450  *
1451  * Args:
1452  *	path   - Path name to the file to load.
1453  *	priv   - Whether the privileged keywords are to be recognized.
1454  *	reload - This is a reload operation
1455  *
1456  * Return:
1457  *	Nothing.
1458  */
1459 void
di_common_parmload(char * path,bool_t priv,bool_t reload)1460 di_common_parmload(char *path, bool_t priv, bool_t reload)
1461 {
1462 	FILE		*fp;
1463 	char		*buf,
1464 			*parm,
1465 			errstr[ERR_BUF_SZ],
1466 			trypath[FILE_PATH_SZ];
1467 	struct utsname	*un;
1468 	bool_t		notry2;
1469 	static bool_t	force_debug;
1470 #ifndef __VMS
1471 	pid_t		cpid;
1472 	waitret_t	wstat;
1473 	int		ret,
1474 			pfd[2];
1475 
1476 	un = util_get_uname();
1477 
1478 	if (priv && di_isdemo())
1479 		app_data.device = "(sim1)";
1480 
1481 	if (PIPE(pfd) < 0) {
1482 		DBGPRN(DBG_GEN)(errfp,
1483 			"di_common_parmload: pipe failed (errno=%d)\n",
1484 			errno);
1485 		if (priv && !di_isdemo()) {
1486 			(void) sprintf(errstr, app_data.str_nocfg, path);
1487 			DI_FATAL(errstr);
1488 		}
1489 		return;
1490 	}
1491 
1492 	switch (cpid = FORK()) {
1493 	case 0:
1494 		/* Child */
1495 
1496 		/* Close un-needed pipe descriptor */
1497 		(void) close(pfd[0]);
1498 
1499 		/* Force uid and gid to original setting */
1500 		if (!util_set_ougid()) {
1501 			(void) close(pfd[1]);
1502 			_exit(1);
1503 		}
1504 
1505 		notry2 = FALSE;
1506 		if (((int) strlen(path) + (int) strlen(un->nodename) + 2)
1507 		    > FILE_PATH_SZ) {
1508 			DBGPRN(DBG_GEN)(errfp, "NOTICE: %s: %s\n",
1509 				"Host-specific config files not used",
1510 				"Name too long");
1511 			(void) strcpy(trypath, path);
1512 			notry2 = TRUE;
1513 		}
1514 		else
1515 			/* Try host-specific config file first */
1516 			(void) sprintf(trypath, "%s-%s", path, un->nodename);
1517 
1518 		DBGPRN(DBG_GEN)(errfp,
1519 			"Loading common parameters: %s\n", trypath);
1520 		fp = fopen(trypath, "r");
1521 
1522 		if (fp == NULL && !notry2) {
1523 			DBGPRN(DBG_GEN)(errfp, "    Cannot open %s\n",
1524 					trypath);
1525 
1526 			/* Try generic config file */
1527 			(void) strcpy(trypath, path);
1528 
1529 			DBGPRN(DBG_GEN)(errfp,
1530 				"Loading common parameters: %s\n",
1531 				trypath);
1532 			fp = fopen(trypath, "r");
1533 		}
1534 
1535 		if (fp == NULL) {
1536 			DBGPRN(DBG_GEN)(errfp, "    Cannot open %s\n",
1537 					trypath);
1538 			(void) close(pfd[1]);
1539 
1540 			if (priv && !di_isdemo())
1541 				_exit(3);
1542 			_exit(0);
1543 		}
1544 
1545 		/* Allocate temporary buffer */
1546 		if ((buf = (char *) MEM_ALLOC("buf", PARM_BUF_SZ)) == NULL) {
1547 			(void) close(pfd[1]);
1548 			(void) fclose(fp);
1549 			_exit(2);
1550 		}
1551 
1552 		while (fgets(buf, PARM_BUF_SZ, fp) != NULL) {
1553 			/* Skip comments and blank lines */
1554 			if (buf[0] == '#' || buf[0] == '!' || buf[0] == '\n')
1555 				continue;
1556 			(void) write(pfd[1], buf, strlen(buf));
1557 		}
1558 		(void) write(pfd[1], ".\n", 2);
1559 
1560 		(void) close(pfd[1]);
1561 		(void) fclose(fp);
1562 
1563 		MEM_FREE(buf);
1564 		_exit(0);
1565 		/*NOTREACHED*/
1566 
1567 	case -1:
1568 		DBGPRN(DBG_GEN)(errfp,
1569 			"di_common_parmload: fork failed (errno=%d)\n",
1570 			errno);
1571 		(void) close(pfd[0]);
1572 		(void) close(pfd[1]);
1573 
1574 		if (priv && !di_isdemo()) {
1575 			(void) sprintf(errstr, app_data.str_nocfg, path);
1576 			DI_FATAL(errstr);
1577 		}
1578 		return;
1579 
1580 	default:
1581 		/* Parent */
1582 
1583 		/* Close un-needed pipe descriptor */
1584 		(void) close(pfd[1]);
1585 
1586 		if ((fp = fdopen(pfd[0], "r")) == NULL) {
1587 			DBGPRN(DBG_GEN)(errfp,
1588 			    "di_common_parmload: read pipe fdopen failed\n");
1589 			if (priv && !di_isdemo()) {
1590 				/* Cannot open pipe */
1591 				(void) sprintf(errstr, app_data.str_nocfg,
1592 					       path);
1593 				DI_FATAL(errstr);
1594 			}
1595 			return;
1596 		}
1597 		break;
1598 	}
1599 #else
1600 	un = util_get_uname();
1601 
1602 	notry2 = FALSE;
1603 	if (((int) strlen(path) + (int) strlen(un->nodename) + 2)
1604 	    > FILE_PATH_SZ) {
1605 		DBGPRN(DBG_GEN)(errfp, "NOTICE: %s: %s\n",
1606 			"Host-specific config files not used",
1607 			"Name too long");
1608 		(void) strcpy(trypath, path);
1609 		notry2 = TRUE;
1610 	}
1611 	else
1612 		/* Try host-specific config file first */
1613 		(void) sprintf(trypath, "%s-%s", path, un->nodename);
1614 
1615 	DBGPRN(DBG_GEN)(errfp, "Loading common parameters: %s\n", trypath);
1616 	fp = fopen(trypath, "r");
1617 
1618 	if (fp == NULL && !notry2) {
1619 		DBGPRN(DBG_GEN)(errfp, "    Cannot open %s\n", trypath);
1620 
1621 		/* Try generic config file */
1622 		(void) strcpy(trypath, path);
1623 
1624 		DBGPRN(DBG_GEN)(errfp,
1625 			"Loading common parameters: %s\n", trypath);
1626 		fp = fopen(trypath, "r");
1627 	}
1628 
1629 	if (fp == NULL) {
1630 		DBGPRN(DBG_GEN)(errfp, "    Cannot open %s\n", trypath);
1631 		if (priv && !di_isdemo()) {
1632 			/* Cannot open system config file. */
1633 			(void) sprintf(errstr, app_data.str_nocfg, path);
1634 			DI_FATAL(errstr);
1635 		}
1636 		return;
1637 	}
1638 #endif	/* __VMS */
1639 
1640 	/* Allocate temporary buffer */
1641 	buf = (char *) MEM_ALLOC("buf", PARM_BUF_SZ);
1642 	parm = (char *) MEM_ALLOC("parmbuf", PARM_BUF_SZ);
1643 	if (buf == NULL || parm == NULL) {
1644 		DI_FATAL(app_data.str_nomemory);
1645 		return;
1646 	}
1647 
1648 	if ((priv && (app_data.debug & DBG_ALL) != 0) || reload)
1649 		force_debug = TRUE;
1650 	else
1651 		force_debug = FALSE;
1652 
1653 	/* Read in common parameters */
1654 	while (fgets(buf, PARM_BUF_SZ, fp) != NULL) {
1655 		/* Skip comments and blank lines */
1656 		if (buf[0] == '#' || buf[0] == '!' || buf[0] == '\n')
1657 			continue;
1658 
1659 		/* Done */
1660 		if (buf[0] == '.' && buf[1] == '\n')
1661 			break;
1662 
1663 		if (priv && sscanf(buf, "device: %[^\n]\n", parm) > 0) {
1664 			if (di_isdemo())
1665 				continue;
1666 
1667 			/* If app_data.device is not NULL, then it means
1668 			 * the user had specified a device on the command
1669 			 * line, and that should take precedence.
1670 			 */
1671 			if (app_data.device == NULL &&
1672 			    !util_newstr(&app_data.device, parm)) {
1673 				DI_FATAL(app_data.str_nomemory);
1674 				return;
1675 			}
1676 			continue;
1677 		}
1678 		if (sscanf(buf, "outputPort: %s\n", parm) > 0) {
1679 			app_data.outport = (word32_t) atoi(parm);
1680 			continue;
1681 		}
1682 		if (sscanf(buf, "cdinfoPath: %[^\n]\n", parm) > 0) {
1683 			if (strcmp(parm, "-") == 0)
1684 				parm[0] = '\0';
1685 			if (!util_newstr(&app_data.cdinfo_path, parm)) {
1686 				DI_FATAL(app_data.str_nomemory);
1687 				return;
1688 			}
1689 			continue;
1690 		}
1691 		if (sscanf(buf, "charsetConvMode: %s\n", parm) > 0) {
1692 			app_data.chset_xlat = atoi(parm);
1693 			continue;
1694 		}
1695 		if (sscanf(buf, "langUtf8: %[^\n]\n", parm) > 0) {
1696 			if (strcmp(parm, "-") == 0)
1697 				parm[0] = '\0';
1698 			if (!util_newstr(&app_data.lang_utf8, parm)) {
1699 				DI_FATAL(app_data.str_nomemory);
1700 				return;
1701 			}
1702 			continue;
1703 		}
1704 		if (sscanf(buf, "acceptFuzzyDefault: %s\n", parm) > 0) {
1705 			app_data.single_fuzzy = util_stob(parm);
1706 			continue;
1707 		}
1708 		if (sscanf(buf, "proxyServer: %[^\n]\n", parm) > 0) {
1709 			if (strcmp(parm, "-") == 0)
1710 				parm[0] = '\0';
1711 			if (!util_newstr(&app_data.proxy_server, parm)) {
1712 				DI_FATAL(app_data.str_nomemory);
1713 				return;
1714 			}
1715 			continue;
1716 		}
1717 		if (sscanf(buf, "cddbCacheTimeout: %s\n", parm) > 0) {
1718 			app_data.cache_timeout = atoi(parm);
1719 			continue;
1720 		}
1721 		if (sscanf(buf, "serviceTimeout: %s\n", parm) > 0) {
1722 			app_data.srv_timeout = atoi(parm);
1723 			continue;
1724 		}
1725 		if (sscanf(buf, "localDiscographyMode: %s\n", parm) > 0) {
1726 			app_data.discog_mode = atoi(parm);
1727 			continue;
1728 		}
1729 		if (sscanf(buf, "maximumHistory: %s\n", parm) > 0) {
1730 			app_data.cdinfo_maxhist = atoi(parm);
1731 			continue;
1732 		}
1733 		if (sscanf(buf, "internetOffline: %s\n", parm) > 0) {
1734 			app_data.cdinfo_inetoffln = util_stob(parm);
1735 			continue;
1736 		}
1737 		if (sscanf(buf, "cddbUseProxy: %s\n", parm) > 0) {
1738 			app_data.use_proxy = util_stob(parm);
1739 			continue;
1740 		}
1741 		if (sscanf(buf, "proxyAuthorization: %s\n", parm) > 0) {
1742 			app_data.proxy_auth = util_stob(parm);
1743 			continue;
1744 		}
1745 		if (sscanf(buf, "autoMusicBrowser: %s\n", parm) > 0) {
1746 			app_data.auto_musicbrowser = util_stob(parm);
1747 			continue;
1748 		}
1749 		if (sscanf(buf, "cdinfoFileMode: %s\n", parm) > 0) {
1750 			if (!util_newstr(&app_data.cdinfo_filemode, parm)) {
1751 				DI_FATAL(app_data.str_nomemory);
1752 				return;
1753 			}
1754 			continue;
1755 		}
1756 		if (sscanf(buf, "historyFileMode: %s\n", parm) > 0) {
1757 			if (!util_newstr(&app_data.hist_filemode, parm)) {
1758 				DI_FATAL(app_data.str_nomemory);
1759 				return;
1760 			}
1761 			continue;
1762 		}
1763 		if (sscanf(buf, "statusPollInterval: %s\n", parm) > 0) {
1764 			app_data.stat_interval = atoi(parm);
1765 			continue;
1766 		}
1767 		if (sscanf(buf, "insertPollInterval: %s\n", parm) > 0) {
1768 			app_data.ins_interval = atoi(parm);
1769 			continue;
1770 		}
1771 		if (sscanf(buf, "insertPollDisable: %s\n", parm) > 0) {
1772 			app_data.ins_disable = util_stob(parm);
1773 			continue;
1774 		}
1775 		if (sscanf(buf, "previousThreshold: %s\n", parm) > 0) {
1776 			app_data.prev_threshold = atoi(parm);
1777 			continue;
1778 		}
1779 		if (sscanf(buf, "sampleBlocks: %s\n", parm) > 0) {
1780 			app_data.sample_blks = atoi(parm);
1781 			continue;
1782 		}
1783 		if (sscanf(buf, "timeDisplayMode: %s\n", parm) > 0) {
1784 			app_data.timedpy_mode = atoi(parm);
1785 			continue;
1786 		}
1787 		if (sscanf(buf, "solaris2VolumeManager: %s\n", parm) > 0) {
1788 			app_data.sol2_volmgt = util_stob(parm);
1789 			continue;
1790 		}
1791 		if (sscanf(buf, "showScsiErrMsg: %s\n", parm) > 0) {
1792 			app_data.scsierr_msg = util_stob(parm);
1793 			continue;
1794 		}
1795 		if (sscanf(buf, "curfileEnable: %s\n", parm) > 0) {
1796 			app_data.write_curfile = util_stob(parm);
1797 			continue;
1798 		}
1799 		if (sscanf(buf, "tooltipEnable: %s\n", parm) > 0) {
1800 			app_data.tooltip_enable = util_stob(parm);
1801 			continue;
1802 		}
1803 		if (sscanf(buf, "tooltipDelayInterval: %s\n", parm) > 0) {
1804 			app_data.tooltip_delay = atoi(parm);
1805 			continue;
1806 		}
1807 		if (sscanf(buf, "tooltipActiveInterval: %s\n", parm) > 0) {
1808 			app_data.tooltip_time = atoi(parm);
1809 			continue;
1810 		}
1811 		if (sscanf(buf, "historyFileDisable: %s\n", parm) > 0) {
1812 			app_data.histfile_dsbl = util_stob(parm);
1813 			continue;
1814 		}
1815 		if (sscanf(buf, "remoteControlEnable: %s\n", parm) > 0) {
1816 			app_data.remote_enb = util_stob(parm);
1817 			continue;
1818 		}
1819 		if (sscanf(buf, "remoteControlLog: %s\n", parm) > 0) {
1820 			app_data.remote_log = util_stob(parm);
1821 			continue;
1822 		}
1823 		if (sscanf(buf, "cddaFilePerTrack: %s\n", parm) > 0) {
1824 			app_data.cdda_trkfile = util_stob(parm);
1825 			continue;
1826 		}
1827 		if (sscanf(buf, "cddaSpaceToUnderscore: %s\n", parm) > 0) {
1828 			app_data.subst_underscore = util_stob(parm);
1829 			continue;
1830 		}
1831 		if (sscanf(buf, "cddaFileFormat: %s\n", parm) > 0) {
1832 			app_data.cdda_filefmt = atoi(parm);
1833 			continue;
1834 		}
1835 		if (sscanf(buf, "cddaFileTemplate: %[^\n]\n", parm) > 0) {
1836 			if (strcmp(parm, "-") == 0)
1837 				parm[0] = '\0';
1838 			if (!util_newstr(&app_data.cdda_tmpl, parm)) {
1839 				DI_FATAL(app_data.str_nomemory);
1840 				return;
1841 			}
1842 			continue;
1843 		}
1844 		if (sscanf(buf, "cddaPipeProgram: %[^\n]\n", parm) > 0) {
1845 			if (strcmp(parm, "-") == 0)
1846 				parm[0] = '\0';
1847 			if (!util_newstr(&app_data.pipeprog, parm)) {
1848 				DI_FATAL(app_data.str_nomemory);
1849 				return;
1850 			}
1851 			continue;
1852 		}
1853 		if (sscanf(buf, "cddaSchedOptions: %s\n", parm) > 0) {
1854 			app_data.cdda_sched = atoi(parm);
1855 			continue;
1856 		}
1857 		if (sscanf(buf, "cddaHeartbeatTimeout: %s\n", parm) > 0) {
1858 			app_data.hb_timeout = atoi(parm);
1859 			continue;
1860 		}
1861 		if (sscanf(buf, "compressionMode: %s\n", parm) > 0) {
1862 			app_data.comp_mode = atoi(parm);
1863 			continue;
1864 		}
1865 		if (sscanf(buf, "compressionBitrate: %s\n", parm) > 0) {
1866 			app_data.bitrate = atoi(parm);
1867 			continue;
1868 		}
1869 		if (sscanf(buf, "minimumBitrate: %s\n", parm) > 0) {
1870 			app_data.bitrate_min = atoi(parm);
1871 			continue;
1872 		}
1873 		if (sscanf(buf, "maximumBitrate: %s\n", parm) > 0) {
1874 			app_data.bitrate_max = atoi(parm);
1875 			continue;
1876 		}
1877 		if (sscanf(buf, "compressionQuality: %s\n", parm) > 0) {
1878 			app_data.qual_factor = atoi(parm);
1879 			continue;
1880 		}
1881 		if (sscanf(buf, "channelMode: %s\n", parm) > 0) {
1882 			app_data.chan_mode = atoi(parm);
1883 			continue;
1884 		}
1885 		if (sscanf(buf, "compressionAlgorithm: %s\n", parm) > 0) {
1886 			app_data.comp_algo = atoi(parm);
1887 			continue;
1888 		}
1889 		if (sscanf(buf, "lowpassMode: %s\n", parm) > 0) {
1890 			app_data.lowpass_mode = atoi(parm);
1891 			continue;
1892 		}
1893 		if (sscanf(buf, "lowpassFrequency: %s\n", parm) > 0) {
1894 			app_data.lowpass_freq = atoi(parm);
1895 			continue;
1896 		}
1897 		if (sscanf(buf, "lowpassWidth: %s\n", parm) > 0) {
1898 			app_data.lowpass_width = atoi(parm);
1899 			continue;
1900 		}
1901 		if (sscanf(buf, "highpassMode: %s\n", parm) > 0) {
1902 			app_data.highpass_mode = atoi(parm);
1903 			continue;
1904 		}
1905 		if (sscanf(buf, "highpassFrequency: %s\n", parm) > 0) {
1906 			app_data.highpass_freq = atoi(parm);
1907 			continue;
1908 		}
1909 		if (sscanf(buf, "highpassWidth: %s\n", parm) > 0) {
1910 			app_data.highpass_width = atoi(parm);
1911 			continue;
1912 		}
1913 		if (sscanf(buf, "copyrightFlag: %s\n", parm) > 0) {
1914 			app_data.copyright = util_stob(parm);
1915 			continue;
1916 		}
1917 		if (sscanf(buf, "originalFlag: %s\n", parm) > 0) {
1918 			app_data.original = util_stob(parm);
1919 			continue;
1920 		}
1921 		if (sscanf(buf, "noBitReservoirFlag: %s\n", parm) > 0) {
1922 			app_data.nores = util_stob(parm);
1923 			continue;
1924 		}
1925 		if (sscanf(buf, "checksumFlag: %s\n", parm) > 0) {
1926 			app_data.checksum = util_stob(parm);
1927 			continue;
1928 		}
1929 		if (sscanf(buf, "strictISO: %s\n", parm) > 0) {
1930 			app_data.strict_iso = util_stob(parm);
1931 			continue;
1932 		}
1933 		if (sscanf(buf, "lameOptionsMode: %s\n", parm) > 0) {
1934 			app_data.lameopts_mode = atoi(parm);
1935 			continue;
1936 		}
1937 		if (sscanf(buf, "lameOptions: %[^\n]\n", parm) > 0) {
1938 			if (strcmp(parm, "-") == 0)
1939 				parm[0] = '\0';
1940 			if (!util_newstr(&app_data.lame_opts, parm)) {
1941 				DI_FATAL(app_data.str_nomemory);
1942 				return;
1943 			}
1944 			continue;
1945 		}
1946 		if (sscanf(buf, "addInfoTag: %s\n", parm) > 0) {
1947 			app_data.add_tag = util_stob(parm);
1948 			continue;
1949 		}
1950 		if (sscanf(buf, "id3TagMode: %s\n", parm) > 0) {
1951 			app_data.id3tag_mode = atoi(parm);
1952 			continue;
1953 		}
1954 		if (sscanf(buf, "spinDownOnLoad: %s\n", parm) > 0) {
1955 			app_data.load_spindown = util_stob(parm);
1956 			continue;
1957 		}
1958 		if (sscanf(buf, "playOnLoad: %s\n", parm) > 0) {
1959 			app_data.load_play = util_stob(parm);
1960 			continue;
1961 		}
1962 		if (sscanf(buf, "ejectOnDone: %s\n", parm) > 0) {
1963 			app_data.done_eject = util_stob(parm);
1964 			continue;
1965 		}
1966 		if (sscanf(buf, "exitOnDone: %s\n", parm) > 0) {
1967 			app_data.done_exit = util_stob(parm);
1968 			continue;
1969 		}
1970 		if (sscanf(buf, "ejectOnExit: %s\n", parm) > 0) {
1971 			app_data.exit_eject = util_stob(parm);
1972 			continue;
1973 		}
1974 		if (sscanf(buf, "stopOnExit: %s\n", parm) > 0) {
1975 			app_data.exit_stop = util_stob(parm);
1976 			continue;
1977 		}
1978 		if (sscanf(buf, "exitOnEject: %s\n", parm) > 0) {
1979 			app_data.eject_exit = util_stob(parm);
1980 			continue;
1981 		}
1982 		if (sscanf(buf, "repeatMode: %s\n", parm) > 0) {
1983 			app_data.repeat_mode = util_stob(parm);
1984 			continue;
1985 		}
1986 		if (sscanf(buf, "shuffleMode: %s\n", parm) > 0) {
1987 			app_data.shuffle_mode = util_stob(parm);
1988 			continue;
1989 		}
1990 		if (sscanf(buf, "discogURLPrefix: %[^\n]\n", parm) > 0) {
1991 			if (strcmp(parm, "-") == 0)
1992 				parm[0] = '\0';
1993 			if (!util_newstr(&app_data.discog_url_pfx, parm)) {
1994 				DI_FATAL(app_data.str_nomemory);
1995 				return;
1996 			}
1997 			continue;
1998 		}
1999 		if (sscanf(buf, "autoMotdDisable: %s\n", parm) > 0) {
2000 			app_data.automotd_dsbl = util_stob(parm);
2001 			continue;
2002 		}
2003 		if (sscanf(buf, "debugLevel: %s\n", parm) > 0) {
2004 			if (!force_debug)
2005 				app_data.debug = (word32_t) atoi(parm);
2006 			continue;
2007 		}
2008 		if (sscanf(buf, "excludeWords: %[^\n]\n", parm) > 0) {
2009 			if (strcmp(parm, "-") == 0)
2010 				parm[0] = '\0';
2011 			if (!util_newstr(&app_data.exclude_words, parm)) {
2012 				DI_FATAL(app_data.str_nomemory);
2013 				return;
2014 			}
2015 			continue;
2016 		}
2017 	}
2018 
2019 	MEM_FREE(buf);
2020 	MEM_FREE(parm);
2021 
2022 	(void) fclose(fp);
2023 
2024 	/* In case of error */
2025 	if (app_data.device == NULL || app_data.device[0] == '\0')
2026 		app_data.device = "/dev/cdrom";
2027 	if (app_data.lang_utf8 == NULL || app_data.lang_utf8[0] == '\0')
2028 		app_data.lang_utf8 = "UTF-8";
2029 	if (app_data.timedpy_mode < 0 ||
2030 	    app_data.timedpy_mode >= TIMEDPY_MAX_MODES)
2031 		app_data.timedpy_mode = 0;
2032 	if (app_data.tooltip_delay < 0)
2033 		app_data.tooltip_delay = 1000;
2034 	if (app_data.tooltip_time < 0)
2035 		app_data.tooltip_delay = 3000;
2036 	if (app_data.stat_interval <= 0)
2037 		app_data.stat_interval = 260;
2038 	if (app_data.cdinfo_maxhist < 0)
2039 		app_data.cdinfo_maxhist = 0;
2040 	if (app_data.cache_timeout <= 0)
2041 		app_data.cache_timeout = DEF_CACHE_TIMEOUT;
2042 	if (app_data.srv_timeout <= 0)
2043 		app_data.srv_timeout = DEF_SRV_TIMEOUT;
2044 	if (app_data.cdda_filefmt < 0 || app_data.cdda_filefmt >= MAX_FILEFMTS)
2045 		app_data.cdda_filefmt = FILEFMT_RAW;
2046 	if (app_data.comp_mode < 0 || app_data.comp_mode > 3)
2047 		app_data.comp_mode = 0;
2048 	if (app_data.qual_factor < 1 || app_data.qual_factor > 10)
2049 		app_data.qual_factor = 3;
2050 	if (app_data.chan_mode < 0 || app_data.chan_mode > 3)
2051 		app_data.chan_mode = 0;
2052 	if (app_data.comp_algo < 1 || app_data.comp_algo > 10)
2053 		app_data.comp_algo = 4;
2054 	if (app_data.lowpass_mode < 0 || app_data.lowpass_mode > 2)
2055 		app_data.lowpass_mode = 0;
2056 	if (app_data.highpass_mode < 0 || app_data.highpass_mode > 2)
2057 		app_data.highpass_mode = 0;
2058 	if (app_data.lowpass_freq < MIN_LOWPASS_FREQ ||
2059 	    app_data.lowpass_freq > MAX_LOWPASS_FREQ)
2060 		app_data.lowpass_freq = MAX_LOWPASS_FREQ;
2061 	if (app_data.lowpass_width < 0)
2062 		app_data.lowpass_width = 0;
2063 	if (app_data.highpass_freq < MIN_HIGHPASS_FREQ ||
2064 	    app_data.highpass_freq > MAX_HIGHPASS_FREQ)
2065 		app_data.highpass_freq = MIN_HIGHPASS_FREQ;
2066 	if (app_data.highpass_width < 0)
2067 		app_data.highpass_width = 0;
2068 	if (app_data.lameopts_mode < 0 || app_data.lameopts_mode > 3)
2069 		app_data.lameopts_mode = 0;
2070 	if (app_data.id3tag_mode < 1 || app_data.id3tag_mode > 3)
2071 		app_data.id3tag_mode = 3;
2072 
2073 	if (app_data.cdda_sched < 0 || app_data.cdda_sched > 3)
2074 		app_data.cdda_sched = 0;
2075 	if (app_data.hb_timeout < MIN_HB_TIMEOUT)
2076 		app_data.hb_timeout = DEF_HB_TIMEOUT;
2077 
2078 	/* playOnLoad overrides spinDownOnLoad */
2079 	if (app_data.load_play)
2080 		app_data.load_spindown = FALSE;
2081 
2082 	/* Check validity of bitrates */
2083 	if (!di_bitrate_valid(app_data.bitrate))
2084 		app_data.bitrate = 0;
2085 	if (!di_bitrate_valid(app_data.bitrate_min))
2086 		app_data.bitrate_min = 0;
2087 	if (!di_bitrate_valid(app_data.bitrate_max))
2088 		app_data.bitrate_min = 0;
2089 	if (app_data.bitrate_min > 0 && app_data.bitrate > 0 &&
2090 	    (app_data.bitrate_min > app_data.bitrate ||
2091 	     app_data.bitrate_min > app_data.bitrate_max))
2092 		app_data.bitrate_min = 0;
2093 	if (app_data.bitrate_max > 0 && app_data.bitrate > 0 &&
2094 	    (app_data.bitrate_max < app_data.bitrate ||
2095 	     app_data.bitrate_max < app_data.bitrate_min))
2096 		app_data.bitrate_min = 0;
2097 
2098 #ifdef __VMS
2099 	/* Force space/tab substitution to underscores on VMS */
2100 	app_data.subst_underscore = TRUE;
2101 #else
2102 	/* Wait for child to exit */
2103 	ret = util_waitchild(cpid, app_data.srv_timeout + 5,
2104 			     NULL, 0, FALSE, &wstat);
2105 	if (ret < 0) {
2106 		DBGPRN(DBG_GEN)(errfp, "di_common_parmload: "
2107 				"waitpid failed (errno=%d)\n", errno);
2108 	}
2109 	else if (WIFEXITED(wstat)) {
2110 		if (WEXITSTATUS(wstat) != 0) {
2111 			DBGPRN(DBG_GEN)(errfp, "di_common_parmload: "
2112 					"child exited (status=%d)\n",
2113 					WEXITSTATUS(wstat));
2114 			(void) sprintf(errstr, app_data.str_nocfg, path);
2115 			if (priv && !di_isdemo()) {
2116 				DI_FATAL(errstr);
2117 			}
2118 			else {
2119 				DI_WARNING(errstr);
2120 			}
2121 		}
2122 	}
2123 	else if (WIFSIGNALED(wstat)) {
2124 		DBGPRN(DBG_GEN)(errfp, "di_common_parmload: "
2125 				"child killed (signal=%d)\n",
2126 				WTERMSIG(wstat));
2127 		(void) sprintf(errstr, app_data.str_nocfg, path);
2128 		if (priv && !di_isdemo()) {
2129 			DI_FATAL(errstr);
2130 		}
2131 		else {
2132 			DI_WARNING(errstr);
2133 		}
2134 	}
2135 #endif
2136 }
2137 
2138 
2139 /*
2140  * di_devspec_parmload
2141  *	Load the specified device-specific configuration file and
2142  *	initialize parameters.
2143  *
2144  * Args:
2145  *	path   - Path name to the file to load.
2146  *	priv   - Whether the privileged keywords are to be recognized.
2147  *	reload - This is a reload operation.
2148  *
2149  * Return:
2150  *	Nothing.
2151  */
2152 /*ARGSUSED*/
2153 void
di_devspec_parmload(char * path,bool_t priv,bool_t reload)2154 di_devspec_parmload(char *path, bool_t priv, bool_t reload)
2155 {
2156 	FILE		*fp;
2157 	char		*buf,
2158 			*parm,
2159 			errstr[ERR_BUF_SZ],
2160 			trypath[FILE_PATH_SZ];
2161 	struct utsname	*un;
2162 	curstat_t	*s = NULL;
2163 	bool_t		notry2;
2164 #ifndef __VMS
2165 	pid_t		cpid;
2166 	waitret_t	wstat;
2167 	int		ret,
2168 			pfd[2];
2169 
2170 	un = util_get_uname();
2171 
2172 	if (di_clinfo != NULL)
2173 		s = di_clinfo->curstat_addr();
2174 
2175 	if (priv && di_isdemo()) {
2176 		char	*cp;
2177 
2178 		cp = "(sim1);(sim2);(sim3);(sim4);(sim5);(sim6);(sim7);(sim8)";
2179 		if (!util_newstr(&app_data.devlist, cp)) {
2180 			DI_FATAL(app_data.str_nomemory);
2181 			return;
2182 		}
2183 		app_data.numdiscs = 8;
2184 		app_data.chg_method = CHG_SCSI_LUN;
2185 		app_data.multi_play = TRUE;
2186 	}
2187 
2188 	if (PIPE(pfd) < 0) {
2189 		DBGPRN(DBG_GEN)(errfp,
2190 			"di_devspec_parmload: pipe failed (errno=%d)\n",
2191 			errno);
2192 		if (priv && !di_isdemo()) {
2193 			(void) sprintf(errstr, app_data.str_nocfg, path);
2194 			DI_FATAL(errstr);
2195 		}
2196 		return;
2197 	}
2198 
2199 	switch (cpid = FORK()) {
2200 	case 0:
2201 		/* Child */
2202 
2203 		/* Close un-needed pipe descriptor */
2204 		(void) close(pfd[0]);
2205 
2206 		/* Force uid and gid to original setting */
2207 		if (!util_set_ougid()) {
2208 			(void) close(pfd[1]);
2209 			_exit(1);
2210 		}
2211 
2212 		notry2 = FALSE;
2213 		if (((int) strlen(path) + (int) strlen(un->nodename) + 2)
2214 		    > FILE_PATH_SZ) {
2215 			DBGPRN(DBG_GEN)(errfp, "NOTICE: %s: %s\n",
2216 				"Host-specific config files not used",
2217 				"Name too long");
2218 			(void) strcpy(trypath, path);
2219 			notry2 = TRUE;
2220 		}
2221 		else
2222 			/* Try host-specific config file first */
2223 			(void) sprintf(trypath, "%s-%s", path, un->nodename);
2224 
2225 		DBGPRN(DBG_GEN)(errfp,
2226 			"Loading device-specific parameters: %s\n", trypath);
2227 
2228 		fp = fopen(trypath, "r");
2229 
2230 		if (fp == NULL && !notry2) {
2231 			DBGPRN(DBG_GEN)(errfp, "    Cannot open %s\n",
2232 					trypath);
2233 
2234 			/* Try generic config file */
2235 			(void) strcpy(trypath, path);
2236 
2237 			DBGPRN(DBG_GEN)(errfp,
2238 				"Loading device-specific parameters: %s\n",
2239 				trypath);
2240 			fp = fopen(trypath, "r");
2241 		}
2242 
2243 		if (fp == NULL) {
2244 			DBGPRN(DBG_GEN)(errfp, "    Cannot open %s\n",
2245 					trypath);
2246 			(void) close(pfd[1]);
2247 
2248 			if (priv && !di_isdemo())
2249 				_exit(3);
2250 			_exit(0);
2251 		}
2252 
2253 		/* Allocate temporary buffer */
2254 		if ((buf = (char *) MEM_ALLOC("buf", PARM_BUF_SZ)) == NULL) {
2255 			(void) close(pfd[1]);
2256 			(void) fclose(fp);
2257 			_exit(2);
2258 		}
2259 
2260 		while (fgets(buf, PARM_BUF_SZ, fp) != NULL) {
2261 			/* Skip comments and blank lines */
2262 			if (buf[0] == '#' || buf[0] == '!' || buf[0] == '\n')
2263 				continue;
2264 			(void) write(pfd[1], buf, strlen(buf));
2265 		}
2266 		(void) write(pfd[1], ".\n", 2);
2267 
2268 		(void) close(pfd[1]);
2269 		(void) fclose(fp);
2270 
2271 		MEM_FREE(buf);
2272 		_exit(0);
2273 		/*NOTREACHED*/
2274 
2275 	case -1:
2276 		DBGPRN(DBG_GEN)(errfp,
2277 			"di_devspec_parmload: fork failed (errno=%d)\n",
2278 			errno);
2279 		(void) close(pfd[0]);
2280 		(void) close(pfd[1]);
2281 
2282 		if (priv && !di_isdemo()) {
2283 			(void) sprintf(errstr, app_data.str_nocfg, path);
2284 			DI_FATAL(errstr);
2285 		}
2286 		return;
2287 
2288 	default:
2289 		/* Parent */
2290 
2291 		/* Close un-needed pipe descriptor */
2292 		(void) close(pfd[1]);
2293 
2294 		if ((fp = fdopen(pfd[0], "r")) == NULL) {
2295 			DBGPRN(DBG_GEN)(errfp,
2296 			    "di_devspec_parmload: read pipe fdopen failed\n");
2297 			if (priv && !di_isdemo()) {
2298 				/* Cannot open pipe */
2299 				(void) sprintf(errstr, app_data.str_nocfg,
2300 					       path);
2301 				DI_FATAL(errstr);
2302 			}
2303 			return;
2304 		}
2305 		break;
2306 	}
2307 #else
2308 	un = util_get_uname();
2309 
2310 	notry2 = FALSE;
2311 	if (((int) strlen(path) + (int) strlen(un->nodename) + 2)
2312 	    > FILE_PATH_SZ) {
2313 		DBGPRN(DBG_GEN)(errfp, "NOTICE: %s: %s\n",
2314 			"Host-specific config files not used",
2315 			"Name too long");
2316 		(void) strcpy(trypath, path);
2317 		notry2 = TRUE;
2318 	}
2319 	else
2320 		/* Try host-specific config file first */
2321 		(void) sprintf(trypath, "%s-%s", path, un->nodename);
2322 
2323 	DBGPRN(DBG_GEN)(errfp,
2324 		"Loading device-specific parameters: %s\n", trypath);
2325 	fp = fopen(trypath, "r");
2326 
2327 	if (fp == NULL && !notry2) {
2328 		DBGPRN(DBG_GEN)(errfp, "    Cannot open %s\n", trypath);
2329 
2330 		/* Try generic config file */
2331 		(void) strcpy(trypath, path);
2332 
2333 		DBGPRN(DBG_GEN)(errfp,
2334 			"Loading device-specific parameters: %s\n", trypath);
2335 		fp = fopen(trypath, "r");
2336 	}
2337 
2338 	if (fp == NULL) {
2339 		DBGPRN(DBG_GEN)(errfp, "    Cannot open %s\n", trypath);
2340 		if (priv && !di_isdemo()) {
2341 			/* Cannot open master device-specific
2342 			 * config file.
2343 			 */
2344 			(void) sprintf(errstr, app_data.str_nocfg, path);
2345 			DI_FATAL(errstr);
2346 		}
2347 		return;
2348 	}
2349 #endif	/* __VMS */
2350 
2351 	/* Allocate temporary buffer */
2352 	buf = (char *) MEM_ALLOC("buf", PARM_BUF_SZ);
2353 	parm = (char *) MEM_ALLOC("parmbuf", PARM_BUF_SZ);
2354 	if (buf == NULL || parm == NULL) {
2355 		DI_FATAL(app_data.str_nomemory);
2356 		return;
2357 	}
2358 
2359 	/* Read in device-specific parameters */
2360 	while (fgets(buf, PARM_BUF_SZ, fp) != NULL) {
2361 		/* Skip comments and blank lines */
2362 		if (buf[0] == '#' || buf[0] == '!' || buf[0] == '\n')
2363 			continue;
2364 
2365 		/* Done */
2366 		if (buf[0] == '.' && buf[1] == '\n')
2367 			break;
2368 
2369 		/* These are privileged parameters and users
2370 		 * cannot overide them in their .xmcdcfg file.
2371 		 */
2372 		if (priv) {
2373 			if (sscanf(buf, "logicalDriveNumber: %s\n",
2374 				   parm) > 0) {
2375 				app_data.devnum = atoi(parm);
2376 				continue;
2377 			}
2378 			if (sscanf(buf, "deviceList: %[^\n]\n", parm) > 0) {
2379 				if (!util_newstr(&app_data.devlist, parm)) {
2380 					DI_FATAL(app_data.str_nomemory);
2381 					return;
2382 				}
2383 				continue;
2384 			}
2385 			if (sscanf(buf, "deviceInterfaceMethod: %s\n",
2386 				   parm) > 0) {
2387 				app_data.di_method = atoi(parm);
2388 				continue;
2389 			}
2390 			if (sscanf(buf, "cddaMethod: %s\n", parm) > 0) {
2391 				app_data.cdda_method = atoi(parm);
2392 				continue;
2393 			}
2394 			if (sscanf(buf, "cddaReadMethod: %s\n", parm) > 0) {
2395 				app_data.cdda_rdmethod = atoi(parm);
2396 				continue;
2397 			}
2398 			if (sscanf(buf, "cddaWriteMethod: %s\n", parm) > 0) {
2399 				app_data.cdda_wrmethod = atoi(parm);
2400 				continue;
2401 			}
2402 			if (sscanf(buf, "cddaScsiModeSelect: %s\n",
2403 				   parm) > 0) {
2404 				app_data.cdda_modesel = util_stob(parm);
2405 				continue;
2406 			}
2407 			if (sscanf(buf, "cddaScsiDensity: %s\n", parm) > 0) {
2408 				app_data.cdda_scsidensity = atoi(parm);
2409 				continue;
2410 			}
2411 			if (sscanf(buf, "cddaScsiReadCommand: %s\n",
2412 				   parm) > 0) {
2413 				app_data.cdda_scsireadcmd = atoi(parm);
2414 				continue;
2415 			}
2416 			if (sscanf(buf, "cddaReadChunkBlocks: %s\n",
2417 				   parm) > 0) {
2418 				app_data.cdda_readchkblks = atoi(parm);
2419 				continue;
2420 			}
2421 			if (sscanf(buf, "cddaDataBigEndian: %s\n", parm) > 0) {
2422 				app_data.cdda_bigendian = util_stob(parm);
2423 				continue;
2424 			}
2425 			if (sscanf(buf, "driveVendorCode: %s\n",
2426 				   parm) > 0) {
2427 				app_data.vendor_code = atoi(parm);
2428 				continue;
2429 			}
2430 			if (sscanf(buf, "scsiVersionCheck: %s\n",
2431 				   parm) > 0) {
2432 				app_data.scsiverck = util_stob(parm);
2433 				continue;
2434 			}
2435 			if (sscanf(buf, "numDiscs: %s\n", parm) > 0) {
2436 				app_data.numdiscs = atoi(parm);
2437 				continue;
2438 			}
2439 			if (sscanf(buf, "mediumChangeMethod: %s\n",
2440 				   parm) > 0) {
2441 				app_data.chg_method = atoi(parm);
2442 				continue;
2443 			}
2444 			if (sscanf(buf, "scsiAudioVolumeBase: %s\n",
2445 				   parm) > 0) {
2446 				app_data.base_scsivol = atoi(parm);
2447 				continue;
2448 			}
2449 			if (sscanf(buf, "minimumPlayBlocks: %s\n", parm) > 0) {
2450 				app_data.min_playblks = atoi(parm);
2451 				continue;
2452 			}
2453 			if (sscanf(buf, "playAudio10Support: %s\n",
2454 				   parm) > 0) {
2455 				app_data.play10_supp = util_stob(parm);
2456 				continue;
2457 			}
2458 			if (sscanf(buf, "playAudio12Support: %s\n",
2459 				   parm) > 0) {
2460 				app_data.play12_supp = util_stob(parm);
2461 				continue;
2462 			}
2463 			if (sscanf(buf, "playAudioMSFSupport: %s\n",
2464 				   parm) > 0) {
2465 				app_data.playmsf_supp = util_stob(parm);
2466 				continue;
2467 			}
2468 			if (sscanf(buf, "playAudioTISupport: %s\n",
2469 				   parm) > 0) {
2470 				app_data.playti_supp = util_stob(parm);
2471 				continue;
2472 			}
2473 			if (sscanf(buf, "loadSupport: %s\n", parm) > 0) {
2474 				app_data.load_supp = util_stob(parm);
2475 				continue;
2476 			}
2477 			if (sscanf(buf, "ejectSupport: %s\n", parm) > 0) {
2478 				app_data.eject_supp = util_stob(parm);
2479 				continue;
2480 			}
2481 			if (sscanf(buf, "modeSenseSetDBD: %s\n", parm) > 0) {
2482 				app_data.msen_dbd = util_stob(parm);
2483 				continue;
2484 			}
2485 			if (sscanf(buf, "modeSenseUse10Byte: %s\n",
2486 				   parm) > 0) {
2487 				app_data.msen_10 = util_stob(parm);
2488 				continue;
2489 			}
2490 			if (sscanf(buf, "volumeControlSupport: %s\n",
2491 				   parm) > 0) {
2492 				app_data.mselvol_supp = util_stob(parm);
2493 				continue;
2494 			}
2495 			if (sscanf(buf, "balanceControlSupport: %s\n",
2496 				   parm) > 0) {
2497 				app_data.balance_supp = util_stob(parm);
2498 				continue;
2499 			}
2500 			if (sscanf(buf, "channelRouteSupport: %s\n",
2501 				   parm) > 0) {
2502 				app_data.chroute_supp = util_stob(parm);
2503 				continue;
2504 			}
2505 			if (sscanf(buf, "pauseResumeSupport: %s\n",
2506 				   parm) > 0) {
2507 				app_data.pause_supp = util_stob(parm);
2508 				continue;
2509 			}
2510 			if (sscanf(buf, "strictPauseResume: %s\n", parm) > 0) {
2511 				app_data.strict_pause_resume = util_stob(parm);
2512 				continue;
2513 			}
2514 			if (sscanf(buf, "playPausePlay: %s\n", parm) > 0) {
2515 				app_data.play_pause_play = util_stob(parm);
2516 				continue;
2517 			}
2518 			if (sscanf(buf, "caddyLockSupport: %s\n", parm) > 0) {
2519 				app_data.caddylock_supp = util_stob(parm);
2520 				continue;
2521 			}
2522 			if (sscanf(buf, "curposFormat: %s\n", parm) > 0) {
2523 				app_data.curpos_fmt = util_stob(parm);
2524 				continue;
2525 			}
2526 			if (sscanf(buf, "noTURWhenPlaying: %s\n", parm) > 0) {
2527 				app_data.play_notur = util_stob(parm);
2528 				continue;
2529 			}
2530 			if (sscanf(buf, "tocLBA: %s\n", parm) > 0) {
2531 				app_data.toc_lba = util_stob(parm);
2532 				continue;
2533 			}
2534 			if (sscanf(buf, "subChannelLBA: %s\n", parm) > 0) {
2535 				app_data.subq_lba = util_stob(parm);
2536 				continue;
2537 			}
2538 			if (sscanf(buf, "driveBlockSize: %s\n", parm) > 0) {
2539 				app_data.drv_blksz = atoi(parm);
2540 				continue;
2541 			}
2542 			if (sscanf(buf, "spinUpInterval: %s\n", parm) > 0) {
2543 				app_data.spinup_interval = atoi(parm);
2544 				continue;
2545 			}
2546 			if (sscanf(buf, "mcnDisable: %s\n", parm) > 0) {
2547 				app_data.mcn_dsbl = util_stob(parm);
2548 				continue;
2549 			}
2550 			if (sscanf(buf, "isrcDisable: %s\n", parm) > 0) {
2551 				app_data.isrc_dsbl = util_stob(parm);
2552 				continue;
2553 			}
2554 			if (sscanf(buf, "cdTextDisable: %s\n", parm) > 0) {
2555 				app_data.cdtext_dsbl = util_stob(parm);
2556 				continue;
2557 			}
2558 		}
2559 
2560 		/* These are general parameters that can be
2561 		 * changed by the user.
2562 		 */
2563 		if (sscanf(buf, "playMode: %s\n", parm) > 0) {
2564 			/* Only allow playmode to change while not playing */
2565 			if (s == NULL ||
2566 			    (s->mode != MOD_PLAY && s->mode != MOD_PAUSE &&
2567 			     s->mode != MOD_SAMPLE)) {
2568 				app_data.play_mode = atoi(parm);
2569 			}
2570 			continue;
2571 		}
2572 		if (sscanf(buf, "volumeControlTaper: %s\n", parm) > 0) {
2573 			app_data.vol_taper = atoi(parm);
2574 			continue;
2575 		}
2576 		if (sscanf(buf, "startupVolume: %s\n", parm) > 0) {
2577 			app_data.startup_vol = atoi(parm);
2578 			continue;
2579 		}
2580 		if (sscanf(buf, "channelRoute: %s\n", parm) > 0) {
2581 			app_data.ch_route = atoi(parm);
2582 			continue;
2583 		}
2584 		if (sscanf(buf, "searchSkipBlocks: %s\n", parm) > 0) {
2585 			app_data.skip_blks = atoi(parm);
2586 			continue;
2587 		}
2588 		if (sscanf(buf, "searchPauseInterval: %s\n", parm) > 0) {
2589 			app_data.skip_pause = atoi(parm);
2590 			continue;
2591 		}
2592 		if (sscanf(buf, "searchSpeedUpCount: %s\n", parm) > 0) {
2593 			app_data.skip_spdup = atoi(parm);
2594 			continue;
2595 		}
2596 		if (sscanf(buf, "searchVolumePercent: %s\n", parm) > 0) {
2597 			app_data.skip_vol = atoi(parm);
2598 			continue;
2599 		}
2600 		if (sscanf(buf, "searchMinVolume: %s\n", parm) > 0) {
2601 			app_data.skip_minvol = atoi(parm);
2602 			continue;
2603 		}
2604 		if (sscanf(buf, "closeOnEject: %s\n", parm) > 0) {
2605 			app_data.eject_close = util_stob(parm);
2606 			continue;
2607 		}
2608 		if (sscanf(buf, "caddyLock: %s\n", parm) > 0) {
2609 			app_data.caddy_lock = util_stob(parm);
2610 			continue;
2611 		}
2612 		if (sscanf(buf, "multiPlay: %s\n", parm) > 0) {
2613 			app_data.multi_play = util_stob(parm);
2614 			continue;
2615 		}
2616 		if (sscanf(buf, "reversePlay: %s\n", parm) > 0) {
2617 			app_data.reverse = util_stob(parm);
2618 			continue;
2619 		}
2620 		if (sscanf(buf, "cddaJitterCorrection: %s\n", parm) > 0) {
2621 			app_data.cdda_jitter_corr = util_stob(parm);
2622 			continue;
2623 		}
2624 	}
2625 
2626 	MEM_FREE(buf);
2627 	MEM_FREE(parm);
2628 
2629 	(void) fclose(fp);
2630 
2631 #ifndef __VMS
2632 	/* Wait for child to exit */
2633 	ret = util_waitchild(cpid, app_data.srv_timeout + 5,
2634 			     NULL, 0, FALSE, &wstat);
2635 	if (ret < 0) {
2636 		DBGPRN(DBG_GEN)(errfp, "di_devspec_parmload: "
2637 				"waitpid failed (errno=%d)\n", errno);
2638 	}
2639 	else if (WIFEXITED(wstat)) {
2640 		if (WEXITSTATUS(wstat) != 0) {
2641 			DBGPRN(DBG_GEN)(errfp, "di_devspec_parmload: "
2642 					"child exited (status=%d)\n",
2643 					WEXITSTATUS(wstat));
2644 			(void) sprintf(errstr, app_data.str_nocfg, path);
2645 			if (priv && !di_isdemo()) {
2646 				DI_FATAL(errstr);
2647 			}
2648 			else {
2649 				DI_WARNING(errstr);
2650 			}
2651 		}
2652 	}
2653 	else if (WIFSIGNALED(wstat)) {
2654 		DBGPRN(DBG_GEN)(errfp, "di_devspec_parmload: "
2655 				"child killed (signal=%d)\n",
2656 				WTERMSIG(wstat));
2657 		(void) sprintf(errstr, app_data.str_nocfg, path);
2658 		if (priv && !di_isdemo()) {
2659 			DI_FATAL(errstr);
2660 		}
2661 		else {
2662 			DI_WARNING(errstr);
2663 		}
2664 	}
2665 #endif
2666 
2667 	if (!priv) {
2668 		/* If the drive does not support software eject, then we
2669 		 * can't lock the caddy.
2670 		 */
2671 		if (!app_data.eject_supp) {
2672 			app_data.caddylock_supp = FALSE;
2673 			app_data.done_eject = FALSE;
2674 			app_data.exit_eject = FALSE;
2675 		}
2676 
2677 		/* If the drive does not support locking the caddy, don't
2678 		 * attempt to lock it.
2679 		 */
2680 		if (!app_data.caddylock_supp)
2681 			app_data.caddy_lock = FALSE;
2682 
2683 		/* If the drive does not support software volume
2684 		 * control, then it can't support the balance
2685 		 * control either.  Also, force the volume control
2686 		 * taper selector to the linear position.
2687 		 */
2688 		if (!app_data.mselvol_supp) {
2689 			app_data.balance_supp = FALSE;
2690 			app_data.vol_taper = VOLTAPER_LINEAR;
2691 		}
2692 
2693 		/* If the drive does not support channel routing,
2694 		 * force the channel routing setting to normal.
2695 		 */
2696 		if (!app_data.chroute_supp)
2697 			app_data.ch_route = 0;
2698 
2699 		/* Other fix-ups as needed */
2700 		if (app_data.numdiscs <= 0)
2701 			app_data.numdiscs = 1;
2702 
2703 		if (app_data.numdiscs == 1) {
2704 			app_data.multi_play = app_data.reverse = FALSE;
2705 		}
2706 
2707 		if (app_data.startup_vol > 100)
2708 			app_data.startup_vol = 100;
2709 		else if (app_data.startup_vol < -1)
2710 			app_data.startup_vol = -1;
2711 
2712 		if (app_data.drv_blksz == 0 ||
2713 		    (app_data.drv_blksz % 512) != 0)
2714 			app_data.drv_blksz = STD_CDROM_BLKSZ;
2715 
2716 		if (app_data.cdda_readchkblks < MIN_CDDA_CHUNK_BLKS ||
2717 		    app_data.cdda_readchkblks > MAX_CDDA_CHUNK_BLKS)
2718 			app_data.cdda_readchkblks = DEF_CDDA_CHUNK_BLKS;
2719 	}
2720 }
2721 
2722 
2723 /*
2724  * di_common_parmsave
2725  *	Save the common configuration parameters to file.
2726  *
2727  * Args:
2728  *	path - Path name to the file to save to.
2729  *
2730  * Return:
2731  *	Nothing.
2732  */
2733 void
di_common_parmsave(char * path)2734 di_common_parmsave(char *path)
2735 {
2736 	FILE		*fp;
2737 	char		*truestr = "True",
2738 			*falsestr = "False",
2739 			*cp,
2740 			filetmpl[FILE_PATH_SZ * 2];
2741 	bool_t		user_tmpl;
2742 #ifndef __VMS
2743 	int		ret;
2744 	pid_t		cpid;
2745 	waitret_t	wstat;
2746 	char		*dirpath,
2747 			errstr[ERR_BUF_SZ];
2748 
2749 	/* Fork child to perform actual I/O */
2750 	switch (cpid = FORK()) {
2751 	case 0:
2752 		/* Child process */
2753 		break;
2754 
2755 	case -1:
2756 		DBGPRN(DBG_GEN)(errfp,
2757 			"di_common_parmsave: fork failed (errno=%d)\n",
2758 			errno);
2759 		return;
2760 
2761 	default:
2762 		/* Parent process: wait for child to exit */
2763 		ret = util_waitchild(cpid, app_data.srv_timeout + 5,
2764 				     NULL, 0, FALSE, &wstat);
2765 		if (ret < 0) {
2766 			DBGPRN(DBG_GEN)(errfp, "di_common_parmsave: "
2767 					"waitpid failed (errno=%d)\n", errno);
2768 		}
2769 		else if (WIFEXITED(wstat)) {
2770 			if (WEXITSTATUS(wstat) != 0) {
2771 				DBGPRN(DBG_GEN)(errfp, "di_common_parmsave: "
2772 						"child exited (status=%d)\n",
2773 						WEXITSTATUS(wstat));
2774 				(void) sprintf(errstr, app_data.str_nocfg,
2775 					       path);
2776 				DI_WARNING(errstr);
2777 			}
2778 		}
2779 		else if (WIFSIGNALED(wstat)) {
2780 			DBGPRN(DBG_GEN)(errfp, "di_common_parmsave: "
2781 					"child killed (signal=%d)\n",
2782 					WTERMSIG(wstat));
2783 			(void) sprintf(errstr, app_data.str_nocfg, path);
2784 			DI_WARNING(errstr);
2785 		}
2786 		return;
2787 	}
2788 
2789 	/* Force uid and gid to original setting */
2790 	if (!util_set_ougid())
2791 		_exit(1);
2792 
2793 	if ((dirpath = util_dirname(path)) == NULL) {
2794 		DI_FATAL("File dirname error");
2795 		return;
2796 	}
2797 	if (!util_mkdir(dirpath, 0755)) {
2798 		DBGPRN(DBG_GEN)(errfp,
2799 			"di_common_parmsave: cannot mkdir %s.\n", dirpath);
2800 		MEM_FREE(dirpath);
2801 		_exit(2);
2802 	}
2803 	MEM_FREE(dirpath);
2804 #endif	/* __VMS */
2805 
2806 	if ((cp = strrchr(app_data.cdda_tmpl, '.')) != NULL)
2807 		*cp = '\0';
2808 
2809 	(void) sprintf(filetmpl,
2810 #ifdef __VMS
2811 		       "%%S.%%C.%%I]%s",
2812 #else
2813 		       "%%S/%%C/%%I/%s",
2814 #endif
2815 		       app_data.cdda_trkfile ? FILEPATH_TRACK : FILEPATH_DISC);
2816 	user_tmpl = (bool_t) (strcmp(app_data.cdda_tmpl, filetmpl) != 0);
2817 
2818 	if (cp != NULL)
2819 		*cp = '.';
2820 
2821 	DBGPRN(DBG_GEN)(errfp, "Writing device-specific file %s\n", path);
2822 
2823 	/* Open file for writing */
2824 	if ((fp = fopen(path, "w")) == NULL) {
2825 		DBGPRN(DBG_GEN)(errfp,
2826 			"di_common_parmsave: cannot open %s.\n", path);
2827 #ifdef __VMS
2828 		return;
2829 #else
2830 		_exit(2);
2831 #endif
2832 	}
2833 
2834 	/* Write banner */
2835 	(void) fprintf(fp, "# xmcd %s.%s Common Configuration File\n",
2836 		       VERSION_MAJ, VERSION_MIN);
2837 	(void) fprintf(fp, "# %s\n# %s\n#\n# %s %s%s\n# %s\n#\n",
2838 		       COPYRIGHT, "Automatically generated -- DO NOT EDIT!",
2839 		       "See the", app_data.libdir, "/config/common.cfg",
2840 		       "file for details about the parameters.");
2841 
2842 	/* Write only parameters configurable via the user interface */
2843 	(void) fprintf(fp, "outputPort:\t\t%d\n", (int) app_data.outport);
2844 	(void) fprintf(fp, "cddaFilePerTrack:\t%s\n",
2845 		       app_data.cdda_trkfile ? truestr : falsestr);
2846 	(void) fprintf(fp, "cddaSpaceToUnderscore:\t%s\n",
2847 		       app_data.subst_underscore ? truestr : falsestr);
2848 	(void) fprintf(fp, "cddaFileFormat:\t\t%d\n", app_data.cdda_filefmt);
2849 	(void) fprintf(fp, "cddaFileTemplate:\t%.1000s\n",
2850 		       user_tmpl ? app_data.cdda_tmpl : "-");
2851 	(void) fprintf(fp, "cddaPipeProgram:\t%.1000s\n",
2852 		       (app_data.pipeprog != NULL) ? app_data.pipeprog : "-");
2853 	(void) fprintf(fp, "cddaSchedOptions:\t%d\n", app_data.cdda_sched);
2854 	(void) fprintf(fp, "cddaHeartbeatTimeout:\t%d\n", app_data.hb_timeout);
2855 	(void) fprintf(fp, "compressionMode:\t%d\n", app_data.comp_mode);
2856 	(void) fprintf(fp, "compressionBitrate:\t%d\n", app_data.bitrate);
2857 	(void) fprintf(fp, "minimumBitrate:\t\t%d\n", app_data.bitrate_min);
2858 	(void) fprintf(fp, "maximumBitrate:\t\t%d\n", app_data.bitrate_max);
2859 	(void) fprintf(fp, "compressionQuality:\t%d\n", app_data.qual_factor);
2860 	(void) fprintf(fp, "channelMode:\t\t%d\n", app_data.chan_mode);
2861 	(void) fprintf(fp, "compressionAlgorithm:\t%d\n", app_data.comp_algo);
2862 	(void) fprintf(fp, "lowpassMode:\t\t%d\n", app_data.lowpass_mode);
2863 	(void) fprintf(fp, "lowpassFrequency:\t%d\n", app_data.lowpass_freq);
2864 	(void) fprintf(fp, "lowpassWidth:\t\t%d\n", app_data.lowpass_width);
2865 	(void) fprintf(fp, "highpassMode:\t\t%d\n", app_data.highpass_mode);
2866 	(void) fprintf(fp, "highpassFrequency:\t%d\n", app_data.highpass_freq);
2867 	(void) fprintf(fp, "highpassWidth:\t\t%d\n", app_data.highpass_width);
2868 	(void) fprintf(fp, "copyrightFlag:\t\t%s\n",
2869 		       app_data.copyright ? truestr : falsestr);
2870 	(void) fprintf(fp, "originalFlag:\t\t%s\n",
2871 		       app_data.original ? truestr : falsestr);
2872 	(void) fprintf(fp, "noBitReservoirFlag:\t%s\n",
2873 		       app_data.nores ? truestr : falsestr);
2874 	(void) fprintf(fp, "checksumFlag:\t\t%s\n",
2875 		       app_data.checksum ? truestr : falsestr);
2876 	(void) fprintf(fp, "strictISO:\t\t%s\n",
2877 		       app_data.strict_iso ? truestr : falsestr);
2878 	(void) fprintf(fp, "lameOptionsMode:\t%d\n", app_data.lameopts_mode);
2879 	(void) fprintf(fp, "lameOptions:\t\t%.1000s\n",
2880 		       (app_data.lame_opts != NULL) ?
2881 		        app_data.lame_opts : "-");
2882 	(void) fprintf(fp, "id3TagMode:\t\t%d\n", app_data.id3tag_mode);
2883 	(void) fprintf(fp, "addInfoTag:\t\t%s\n",
2884 		       app_data.add_tag ? truestr : falsestr);
2885 	(void) fprintf(fp, "spinDownOnLoad:\t\t%s\n",
2886 		       app_data.load_spindown ? truestr : falsestr);
2887 	(void) fprintf(fp, "playOnLoad:\t\t%s\n",
2888 		       app_data.load_play ? truestr : falsestr);
2889 	(void) fprintf(fp, "ejectOnDone:\t\t%s\n",
2890 		       app_data.done_eject ? truestr : falsestr);
2891 	(void) fprintf(fp, "exitOnDone:\t\t%s\n",
2892 		       app_data.done_exit ? truestr : falsestr);
2893 	(void) fprintf(fp, "ejectOnExit:\t\t%s\n",
2894 		       app_data.exit_eject ? truestr : falsestr);
2895 	(void) fprintf(fp, "stopOnExit:\t\t%s\n",
2896 		       app_data.exit_stop ? truestr : falsestr);
2897 	(void) fprintf(fp, "exitOnEject:\t\t%s\n",
2898 		       app_data.eject_exit ? truestr : falsestr);
2899 	(void) fprintf(fp, "repeatMode:\t\t%s\n",
2900 		       app_data.repeat_mode ? truestr : falsestr);
2901 	(void) fprintf(fp, "shuffleMode:\t\t%s\n",
2902 		       app_data.shuffle_mode ? truestr : falsestr);
2903 	(void) fprintf(fp, "cdinfoPath:\t\t%s\n", app_data.cdinfo_path);
2904 	(void) fprintf(fp, "charsetConvMode:\t%d\n",
2905 		       app_data.chset_xlat);
2906 	(void) fprintf(fp, "langUtf8:\t\t%s\n", app_data.lang_utf8);
2907 	(void) fprintf(fp, "acceptFuzzyDefault:\t%s\n",
2908 		       app_data.single_fuzzy ? truestr : falsestr);
2909 	(void) fprintf(fp, "cddbUseProxy:\t\t%s\n",
2910 		       app_data.use_proxy ? truestr : falsestr);
2911 	(void) fprintf(fp, "proxyServer:\t\t%s\n", app_data.proxy_server);
2912 	(void) fprintf(fp, "proxyAuthorization:\t%s\n",
2913 		       app_data.proxy_auth ? truestr : falsestr);
2914 	(void) fprintf(fp, "autoMusicBrowser:\t%s\n",
2915 		       app_data.auto_musicbrowser ? truestr : falsestr);
2916 	(void) fprintf(fp, "cddbCacheTimeout:\t%d\n", app_data.cache_timeout);
2917 	(void) fprintf(fp, "serviceTimeout:\t\t%d\n", app_data.srv_timeout);
2918 	(void) fprintf(fp, "autoMotdDisable:\t%s\n",
2919 		       app_data.automotd_dsbl ? truestr : falsestr);
2920 
2921 	if (fclose(fp) != 0) {
2922 #ifdef __VMS
2923 		return;
2924 #else
2925 		_exit(1);
2926 #endif
2927 	}
2928 
2929 	(void) chmod(path, 0644);
2930 
2931 	/* Child exits here */
2932 #ifndef __VMS
2933 	_exit(0);
2934 #endif
2935 	/*NOTREACHED*/
2936 }
2937 
2938 
2939 /*
2940  * di_devspec_parmsave
2941  *	Save the device-specific configuration parameters to file.
2942  *
2943  * Args:
2944  *	path - Path name to the file to save to.
2945  *
2946  * Return:
2947  *	Nothing.
2948  */
2949 void
di_devspec_parmsave(char * path)2950 di_devspec_parmsave(char *path)
2951 {
2952 	FILE		*fp;
2953 	char		*truestr = "True",
2954 			*falsestr = "False";
2955 #ifndef __VMS
2956 	int		ret;
2957 	pid_t		cpid;
2958 	waitret_t	wstat;
2959 	char		*dirpath,
2960 			errstr[ERR_BUF_SZ];
2961 
2962 	/* Fork child to perform actual I/O */
2963 	switch (cpid = FORK()) {
2964 	case 0:
2965 		/* Child process */
2966 		break;
2967 
2968 	case -1:
2969 		DBGPRN(DBG_GEN)(errfp,
2970 			"di_devspec_parmsave: fork failed (errno=%d)\n",
2971 			errno);
2972 		return;
2973 
2974 	default:
2975 		/* Parent process: wait for child to exit */
2976 		ret = util_waitchild(cpid, app_data.srv_timeout + 5,
2977 				     NULL, 0, FALSE, &wstat);
2978 		if (ret < 0) {
2979 			DBGPRN(DBG_GEN)(errfp, "di_devspec_parmsave: "
2980 					"waitpid failed (errno=%d)\n", errno);
2981 		}
2982 		else if (WIFEXITED(wstat)) {
2983 			if (WEXITSTATUS(wstat) != 0) {
2984 				DBGPRN(DBG_GEN)(errfp, "di_devspec_parmsave: "
2985 						"child exited (status=%d)\n",
2986 						WEXITSTATUS(wstat));
2987 				(void) sprintf(errstr, app_data.str_nocfg,
2988 					       path);
2989 				DI_WARNING(errstr);
2990 			}
2991 		}
2992 		else if (WIFSIGNALED(wstat)) {
2993 			DBGPRN(DBG_GEN)(errfp, "di_devspec_parmsave: "
2994 					"child killed (signal=%d)\n",
2995 					WTERMSIG(wstat));
2996 			(void) sprintf(errstr, app_data.str_nocfg, path);
2997 			DI_WARNING(errstr);
2998 		}
2999 		return;
3000 	}
3001 
3002 	/* Force uid and gid to original setting */
3003 	if (!util_set_ougid())
3004 		_exit(1);
3005 
3006 	if ((dirpath = util_dirname(path)) == NULL) {
3007 		DI_FATAL("File dirname error");
3008 		return;
3009 	}
3010 	if (!util_mkdir(dirpath, 0755)) {
3011 		DBGPRN(DBG_GEN)(errfp,
3012 			"di_devspec_parmsave: cannot mkdir %s.\n", dirpath);
3013 		MEM_FREE(dirpath);
3014 		_exit(2);
3015 	}
3016 	MEM_FREE(dirpath);
3017 #endif	/* __VMS */
3018 
3019 	DBGPRN(DBG_GEN)(errfp, "Writing device-specific file %s\n", path);
3020 
3021 	/* Open file for writing */
3022 	if ((fp = fopen(path, "w")) == NULL) {
3023 		DBGPRN(DBG_GEN)(errfp,
3024 			"di_devspec_parmsave: cannot open %s.\n", path);
3025 #ifdef __VMS
3026 		return;
3027 #else
3028 		_exit(2);
3029 #endif
3030 	}
3031 
3032 	/* Write banner */
3033 	(void) fprintf(fp, "# xmcd %s.%s Device-Specific Configuration File\n",
3034 		       VERSION_MAJ, VERSION_MIN);
3035 	(void) fprintf(fp, "# %s\n# %s\n#\n# %s %s%s\n# %s\n#\n",
3036 		       COPYRIGHT, "Automatically generated -- DO NOT EDIT!",
3037 		       "See the", app_data.libdir, "/config/device.cfg",
3038 		       "file for details about the parameters.");
3039 
3040 	/* Write only user-changeable parameters */
3041 	(void) fprintf(fp, "playMode:\t\t%d\n", app_data.play_mode);
3042 	(void) fprintf(fp, "cddaJitterCorrection:\t%s\n",
3043 		       app_data.cdda_jitter_corr ? truestr : falsestr);
3044 	(void) fprintf(fp, "volumeControlTaper:\t%d\n", app_data.vol_taper);
3045 	(void) fprintf(fp, "channelRoute:\t\t%d\n", app_data.ch_route);
3046 	(void) fprintf(fp, "caddyLock:\t\t%s\n",
3047 		       app_data.caddy_lock ? truestr : falsestr);
3048 	(void) fprintf(fp, "multiPlay:\t\t%s\n",
3049 		       app_data.multi_play ? truestr : falsestr);
3050 	(void) fprintf(fp, "reversePlay:\t\t%s\n",
3051 		       app_data.reverse ? truestr : falsestr);
3052 
3053 	if (fclose(fp) != 0) {
3054 #ifdef __VMS
3055 		return;
3056 #else
3057 		_exit(1);
3058 #endif
3059 	}
3060 
3061 	(void) chmod(path, 0644);
3062 
3063 	/* Child exits here */
3064 #ifndef __VMS
3065 	_exit(0);
3066 #endif
3067 	/*NOTREACHED*/
3068 }
3069 
3070 
3071 /*
3072  * di_devlock
3073  *	Create a lock to prevent another cooperating CD audio process
3074  *	from accessing the same CD-ROM device.
3075  *
3076  * Args:
3077  *	s - Pointer to the curstat_t structure.
3078  *	path - The CD-ROM device node path name.
3079  *
3080  * Return:
3081  *	TRUE if the lock was successful.  If FALSE, then it indicates
3082  *	that another xmcd process currently has the lock.
3083  */
3084 /*ARGSUSED*/
3085 bool_t
di_devlock(curstat_t * s,char * path)3086 di_devlock(curstat_t *s, char *path)
3087 {
3088 #ifndef __VMS
3089 /* UNIX */
3090 	int		fd;
3091 	pid_t		pid,
3092 			mypid;
3093 	char		buf[32];
3094 	struct stat	stbuf;
3095 
3096 	if (di_isdemo())
3097 		return TRUE;	/* No locking needed in demo mode */
3098 
3099 	if (stat(path, &stbuf) < 0) {
3100 		if (errno == ENOENT)
3101 			/* If the device node is missing it is probably
3102 			 * due to dynamic node creation/removal
3103 			 * on some platforms based on presence of media.
3104 			 * In this case, let the lock call succeed without
3105 			 * actually setting a lock.  Otherwise the status
3106 			 * would erroneously become "cd busy".
3107 			 */
3108 			return TRUE;
3109 		else
3110 			return FALSE;
3111 	}
3112 
3113 	(void) sprintf(lockfile, "%s/lock.%x", TEMP_DIR, (int) stbuf.st_rdev);
3114 
3115 	DBGPRN(DBG_GEN)(errfp, "\nLock file: %s\n", lockfile);
3116 
3117 	mypid = getpid();
3118 
3119 	for (;;) {
3120 		fd = open(lockfile, O_CREAT | O_EXCL | O_WRONLY);
3121 		if (fd < 0) {
3122 			if (errno == EEXIST) {
3123 				fd = open(lockfile, O_RDONLY
3124 #ifdef O_NOFOLLOW
3125 						    | O_NOFOLLOW
3126 #endif
3127 				);
3128 				if (fd < 0)
3129 					return FALSE;
3130 
3131 				if (read(fd, buf, 32) > 0) {
3132 					if (strncmp(buf, "cdlk ", 5) != 0){
3133 						(void) close(fd);
3134 						return FALSE;
3135 					}
3136 					pid = (pid_t) atoi(buf + 5);
3137 				}
3138 				else {
3139 					(void) close(fd);
3140 					return FALSE;
3141 				}
3142 
3143 				(void) close(fd);
3144 
3145 				if (pid == mypid)
3146 					/* Our own lock */
3147 					return TRUE;
3148 
3149 				if (pid <= 0 ||
3150 				    (kill(pid, 0) < 0 && errno == ESRCH)) {
3151 					/* Pid died, steal its lockfile */
3152 					(void) UNLINK(lockfile);
3153 				}
3154 				else {
3155 					/* Pid still running: clash */
3156 					return FALSE;
3157 				}
3158 			}
3159 			else
3160 				return FALSE;
3161 		}
3162 		else {
3163 			(void) sprintf(buf, "cdlk %d\n", (int) mypid);
3164 			(void) write(fd, buf, strlen(buf));
3165 
3166 #ifdef NO_FCHMOD
3167 			(void) close(fd);
3168 			(void) chmod(lockfile, 0644);
3169 #else
3170 			(void) fchmod(fd, 0644);
3171 			(void) close(fd);
3172 #endif
3173 
3174 			return TRUE;
3175 		}
3176 	}
3177 #else
3178 /* OpenVMS */
3179 	extern int		SYS$GETDVIW();
3180 	static char		ref_cnt;
3181 	int			status;
3182 
3183 	struct {
3184 		short		buflen;
3185 		short		code;
3186 		char		*bufadr;
3187 		int		*length;
3188 		int		terminator;
3189 	} itemlist = {
3190 		4,
3191 		DVI$_REFCNT,
3192 		&ref_cnt,
3193 		NULL,
3194 		0
3195 	};
3196 
3197         struct dsc$descriptor	dev_nam_desc;
3198 
3199 	if (path == NULL)
3200 		return TRUE;
3201 
3202 	ref_cnt = 1;
3203 
3204 	dev_nam_desc.dsc$b_class = DSC$K_CLASS_S;
3205 	dev_nam_desc.dsc$b_dtype = DSC$K_DTYPE_T;
3206 	dev_nam_desc.dsc$a_pointer = path;
3207 	dev_nam_desc.dsc$w_length = strlen(path);
3208 
3209 	status = SYS$GETDVIW(0, 0, &dev_nam_desc, &itemlist, 0, 0, 0, 0);
3210 
3211 	if (status != 1) {
3212 		(void) fprintf(errfp,
3213 		       "CD audio: Cannot get information on device: %s\n",
3214 			    path);
3215 		return FALSE;
3216 	}
3217 
3218 	return ((bool_t) (ref_cnt == 0));
3219 #endif	/* __VMS */
3220 }
3221 
3222 
3223 /*
3224  * di_devunlock
3225  *	Unlock the lock that was created with di_devlock().
3226  *
3227  * Args:
3228  *	s - Pointer to the curstat_t structure.
3229  *
3230  * Return:
3231  *	Nothing.
3232  */
3233 /*ARGSUSED*/
3234 void
di_devunlock(curstat_t * s)3235 di_devunlock(curstat_t *s)
3236 {
3237 #ifndef __VMS
3238 	if (lockfile[0] != '\0' && s->devlocked)
3239 		(void) UNLINK(lockfile);
3240 #endif
3241 }
3242 
3243 
3244 /*
3245  * di_devalloc
3246  *	Allocate a device descriptor structure.
3247  *
3248  * Args:
3249  *	path - The device path name (required)
3250  *
3251  * Return:
3252  *	Pointer to the allocated structure, or NULL on failure.
3253  */
3254 di_dev_t *
di_devalloc(char * path)3255 di_devalloc(char *path)
3256 {
3257 	di_dev_t	*devp;
3258 
3259 	if (path == NULL || *path == '\0')
3260 		return FALSE;
3261 
3262 	devp = (di_dev_t *)(void *) MEM_ALLOC(
3263 		"di_dev_t",
3264 		sizeof(di_dev_t)
3265 	);
3266 	if (devp == NULL)
3267 		return NULL;
3268 
3269 	(void) memset(devp, 0, sizeof(di_dev_t));
3270 
3271 	devp->fd = -1;
3272 	devp->path = NULL;
3273 	if (!util_newstr(&devp->path, path))
3274 		return NULL;
3275 
3276 	return (devp);
3277 }
3278 
3279 
3280 /*
3281  * di_devfree
3282  *	De-allocate a device descriptor structure.
3283  *
3284  * Args:
3285  *	devp - Pointer to the device descriptor.
3286  *
3287  * Return:
3288  *	Nothing.
3289  */
3290 void
di_devfree(di_dev_t * devp)3291 di_devfree(di_dev_t *devp)
3292 {
3293 	MEM_FREE(devp->path);
3294 	MEM_FREE(devp);
3295 }
3296 
3297 
3298 /*
3299  * di_methodstr
3300  *	Return a text string indicating the current operating mode.
3301  *
3302  * Args:
3303  *	Nothing.
3304  *
3305  * Return:
3306  *	Mode text string.
3307  */
3308 char *
di_methodstr(void)3309 di_methodstr(void)
3310 {
3311 	char		*p;
3312 	static char	str[STR_BUF_SZ * 6];
3313 
3314 	if (ditbl[app_data.di_method].methodstr != NULL)
3315 		p = (ditbl[app_data.di_method].methodstr());
3316 	else
3317 		p = "";
3318 
3319 	(void) sprintf(str, "Device interface method: %s\n%s",
3320 			p, cdda_info());
3321 	return (str);
3322 }
3323 
3324 
3325 /*
3326  * di_isdemo
3327  *	Query if this is a demo-only version of the CD player.
3328  *
3329  * Args:
3330  *	Nothing.
3331  *
3332  * Return:
3333  *	TRUE - demo-only version.
3334  *	FALSE - real version.
3335  */
3336 bool_t
di_isdemo(void)3337 di_isdemo(void)
3338 {
3339 #ifdef DEMO_ONLY
3340 	return TRUE;
3341 #else
3342 	return FALSE;
3343 #endif
3344 }
3345 
3346 
3347 /*
3348  * di_curtrk_pos
3349  *	Return the trkinfo table offset location of the current playing
3350  *	CD track.
3351  *
3352  * Args:
3353  *	s - Pointer to the curstat_t structure.
3354  *
3355  * Return:
3356  *	Integer offset into the trkinfo table, or -1 if not currently
3357  *	playing audio.
3358  */
3359 int
di_curtrk_pos(curstat_t * s)3360 di_curtrk_pos(curstat_t *s)
3361 {
3362 	int	i;
3363 
3364 	if ((int) s->cur_trk <= 0)
3365 		return -1;
3366 
3367 	i = (int) s->cur_trk - 1;
3368 
3369 	if (s->trkinfo[i].trkno == s->cur_trk)
3370 		return (i);
3371 
3372 	for (i = 0; i < MAXTRACK; i++) {
3373 		if (s->trkinfo[i].trkno == s->cur_trk)
3374 			return (i);
3375 	}
3376 	return -1;
3377 }
3378 
3379 
3380 /*
3381  * di_curprog_pos
3382  *	Return an integer representing the position of the current
3383  *	program or shuffle mode playing order (0 = first, 1 = second, ...).
3384  *	This routine should be used only when in program or shuffle play
3385  *	mode.
3386  *
3387  * Arg:
3388  *	s - Pointer to the curstat_t structure.
3389  *
3390  * Return:
3391  *	An integer representing the position of the current program
3392  *	or shuffle mode playing order, or -1 if not in the appropriate mode.
3393  */
3394 int
di_curprog_pos(curstat_t * s)3395 di_curprog_pos(curstat_t *s)
3396 {
3397 	return ((int) s->trkinfo[s->prog_cnt].playorder);
3398 }
3399 
3400 
3401 /*
3402  * di_clear_cdinfo
3403  *	Clear in-core CD information
3404  *
3405  * Args:
3406  *	s - Pointer to the curstat_t structure
3407  *	reload - Whether we're going to be reloading the CD information
3408  *
3409  * Return:
3410  *	Nothing.
3411  */
3412 void
di_clear_cdinfo(curstat_t * s,bool_t reload)3413 di_clear_cdinfo(curstat_t *s, bool_t reload)
3414 {
3415 	DBCLEAR(s, reload);
3416 	di_cdinfo_loaded = FALSE;
3417 }
3418 
3419 
3420 /*
3421  * di_get_cdinfo
3422  *	Look up CD information
3423  *
3424  * Args:
3425  *	s - Pointer to the curstat_t structure
3426  *
3427  * Return:
3428  *	CD information lookup status (QMODE_xxx from appenv.h)
3429  */
3430 byte_t
di_get_cdinfo(curstat_t * s)3431 di_get_cdinfo(curstat_t *s)
3432 {
3433 	if (di_cdinfo_loaded)
3434 		return TRUE;
3435 
3436 	/* Call into the client to do the actual lookup */
3437 	DBGET(s);
3438 	di_cdinfo_loaded = TRUE;
3439 
3440 	return (s->qmode);
3441 }
3442 
3443 
3444 /*
3445  * di_prepare_cdda
3446  *	Prepare for CDDA operation
3447  *
3448  * Args:
3449  *	s - Pointer to the curstat_t structure
3450  *
3451  * Return:
3452  *	TRUE  - success
3453  *	FALSE - failure
3454  */
3455 bool_t
di_prepare_cdda(curstat_t * s)3456 di_prepare_cdda(curstat_t *s)
3457 {
3458 	if (PLAYMODE_IS_STD(app_data.play_mode))
3459 		return TRUE;
3460 
3461 	/* Expand CDDA output file pathnames if necessary */
3462 	if ((app_data.play_mode & PLAYMODE_FILE) != 0 &&
3463 	    di_clinfo->mkoutpath != NULL) {
3464 		if (di_chktmpl(s, "%C") ||
3465 		    di_chktmpl(s, "%A") || di_chktmpl(s, "%a") ||
3466 		    di_chktmpl(s, "%D") || di_chktmpl(s, "%d") ||
3467 		    di_chktmpl(s, "%R") || di_chktmpl(s, "%r") ||
3468 		    di_chktmpl(s, "%T") || di_chktmpl(s, "%t") ||
3469 		    di_chktmpl(s, "%B") || di_chktmpl(s, "%b"))
3470 			(void) di_get_cdinfo(s);
3471 
3472 		if (!di_clinfo->mkoutpath(s))
3473 			return FALSE;
3474 	}
3475 
3476 	/* Check CDDA pipe program if necessary */
3477 	if ((app_data.play_mode & PLAYMODE_PIPE) != 0 &&
3478 	    di_clinfo->ckpipeprog != NULL && !di_clinfo->ckpipeprog(s))
3479 		return FALSE;
3480 
3481 	return TRUE;
3482 }
3483 
3484 
3485 /*
3486  * di_reset_curstat
3487  *	Reset the curstat_t structure to initial defaults.
3488  *
3489  * Args:
3490  *	s - Pointer to the curstat_t structure.
3491  *	clear_toc - Whether the trkinfo CD table-of-contents
3492  *		should be cleared.
3493  *	eject - Whether the medium is being ejected
3494  *
3495  * Return:
3496  *	Nothing.
3497  */
3498 void
di_reset_curstat(curstat_t * s,bool_t clear_toc,bool_t eject)3499 di_reset_curstat(curstat_t *s, bool_t clear_toc, bool_t eject)
3500 {
3501 	sword32_t	i;
3502 	static bool_t	first_time = TRUE;
3503 
3504 	s->cur_trk = s->cur_idx = -1;
3505 	s->curpos_tot.min = s->curpos_tot.sec = s->curpos_tot.frame = 0;
3506 	s->curpos_trk.min = s->curpos_trk.sec = s->curpos_trk.frame = 0;
3507 	s->curpos_tot.addr = s->curpos_trk.addr = 0;
3508 	s->sav_iaddr = 0;
3509 	s->prog_cnt = 0;
3510 	s->frm_played = s->tot_frm = 0;
3511 	s->frm_per_sec = 0;
3512 
3513 	if (s->onetrk_prog) {
3514 		s->onetrk_prog = FALSE;
3515 		PROGCLEAR(s);
3516 	}
3517 
3518 	if (clear_toc) {
3519 		s->flags = 0;
3520 		s->segplay = SEGP_NONE;
3521 		s->first_trk = s->last_trk = -1;
3522 		s->discpos_tot.min = s->discpos_tot.sec = 0;
3523 		s->tot_trks = 0;
3524 		s->discpos_tot.addr = 0;
3525 		s->prog_tot = 0;
3526 		s->program = FALSE;
3527 
3528 		(void) memset(s->mcn, 0, sizeof(s->mcn));
3529 
3530 		for (i = 0; i < MAXTRACK; i++) {
3531 			s->trkinfo[i].trkno = -1;
3532 			s->trkinfo[i].min = 0;
3533 			s->trkinfo[i].sec = 0;
3534 			s->trkinfo[i].frame = 0;
3535 			s->trkinfo[i].addr = 0;
3536 			s->trkinfo[i].type = TYP_AUDIO;
3537 			s->trkinfo[i].playorder = -1;
3538 			if (s->trkinfo[i].outfile != NULL) {
3539 				MEM_FREE(s->trkinfo[i].outfile);
3540 				s->trkinfo[i].outfile = NULL;
3541 			}
3542 
3543 			(void) memset(s->trkinfo[i].isrc, 0,
3544 				      sizeof(s->trkinfo[i].isrc));
3545 		}
3546 	}
3547 
3548 	if (eject) {
3549 		s->mode = MOD_NODISC;
3550 		s->rptcnt = 0;
3551 	}
3552 
3553 	if (first_time) {
3554 		/* These are to be initialized only once */
3555 		first_time = FALSE;
3556 
3557 		s->first_disc = 1;
3558 		s->last_disc = app_data.numdiscs;
3559 		s->cur_disc = app_data.reverse ? s->last_disc : s->first_disc;
3560 		s->prev_disc = -1;
3561 		s->time_dpy = T_ELAPSED_TRACK;
3562 		s->repeat = s->shuffle = FALSE;
3563 		s->rptcnt = 0;
3564 		s->level = 0;
3565 		s->level_left = s->level_right = 100;
3566 		s->cdda_att = 100;
3567 		s->caddy_lock = FALSE;
3568 		s->vendor[0] = '\0';
3569 		s->prod[0] = '\0';
3570 		s->revnum[0] = '\0';
3571 	}
3572 }
3573 
3574 
3575 /*
3576  * di_reset_shuffle
3577  *	Recompute a new shuffle play sequence.  Updates the playorder
3578  *	table in the curstat_t structure.
3579  *
3580  * Args:
3581  *	s - Pointer to the curstat_t structure.
3582  *
3583  * Return:
3584  *	Nothing.
3585  */
3586 void
di_reset_shuffle(curstat_t * s)3587 di_reset_shuffle(curstat_t *s)
3588 {
3589 	sword32_t	i,
3590 			j,
3591 			n,
3592 			x;
3593 
3594 	srand((unsigned) time(NULL));
3595 	s->prog_cnt = 0;
3596 
3597 	/* Initialize the play-list with only audio tracks */
3598 	n = 0;
3599 	for (i = 0; i < (int) s->tot_trks; i++) {
3600 		if (s->trkinfo[i].type == TYP_AUDIO)
3601 			s->trkinfo[n++].playorder = i;
3602 	}
3603 
3604 	s->prog_tot = (byte_t) n;
3605 
3606 	/* Initialize rest of list */
3607 	for (; n < MAXTRACK; n++)
3608 		s->trkinfo[n].playorder = -1;
3609 
3610 	/*
3611 	 * Now shuffle the playlist by swapping random pairs:
3612 	 * this is the most efficient alogorithm for a linear
3613 	 * distribution.
3614 	 */
3615 	for (i = (int) s->prog_tot - 1; i > 0; i--) {
3616 		j = rand() % (i + 1);
3617 		if (j != i) {
3618 			x = s->trkinfo[i].playorder;
3619 			s->trkinfo[i].playorder = s->trkinfo[j].playorder;
3620 			s->trkinfo[j].playorder = x;
3621 		}
3622 	}
3623 
3624 	if (app_data.debug & DBG_GEN) {
3625 		(void) fprintf(errfp, "\nShuffle tracks: ");
3626 
3627 		for (i = 0; i < (int) s->prog_tot; i++)
3628 			(void) fprintf(errfp, "%d ",
3629 				   s->trkinfo[s->trkinfo[i].playorder].trkno);
3630 
3631 		(void) fprintf(errfp, "\n");
3632 	}
3633 }
3634 
3635 
3636