mirror of
				https://github.com/alsa-project/alsa-lib.git
				synced 2025-11-03 09:01:52 -05:00 
			
		
		
		
	Enhanced configuration syntax (added [ ] block for arrays).
The snd_config_expand functions expands the runtime contents (@func...). Removed the environment variable replace code from the configuration parser. Updated the alsa.conf configuration file.
This commit is contained in:
		
							parent
							
								
									bf780a25a5
								
							
						
					
					
						commit
						5c3075d32f
					
				
					 9 changed files with 1062 additions and 485 deletions
				
			
		
							
								
								
									
										244
									
								
								src/alsa.conf
									
										
									
									
									
								
							
							
						
						
									
										244
									
								
								src/alsa.conf
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -6,22 +6,36 @@ pcm.default {
 | 
			
		|||
	type plug
 | 
			
		||||
	slave.pcm {
 | 
			
		||||
		type hw
 | 
			
		||||
		card <@ALSA_PCM_CARD,ALSA_CARD:0@>
 | 
			
		||||
		device <@ALSA_PCM_DEVICE:0@>
 | 
			
		||||
		card {
 | 
			
		||||
			@func getenv
 | 
			
		||||
			@type integer
 | 
			
		||||
			envname [
 | 
			
		||||
				ALSA_PCM_CARD
 | 
			
		||||
				ALSA_CARD
 | 
			
		||||
			]
 | 
			
		||||
			default 0
 | 
			
		||||
		}
 | 
			
		||||
		device {
 | 
			
		||||
			@func getenv
 | 
			
		||||
			@type integer
 | 
			
		||||
			envname [
 | 
			
		||||
				ALSA_PCM_DEVICE
 | 
			
		||||
			]
 | 
			
		||||
			default 0
 | 
			
		||||
		}
 | 
			
		||||
		subdevice -1
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pcm.hw {
 | 
			
		||||
	$.0 CARD
 | 
			
		||||
	$.1 DEV
 | 
			
		||||
	$.2 SUBDEV
 | 
			
		||||
	$.CARD {
 | 
			
		||||
	args [ CARD DEV SUBDEV ]
 | 
			
		||||
	args.CARD {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	$.DEV {
 | 
			
		||||
	args.DEV {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	$.SUBDEV {
 | 
			
		||||
	args.SUBDEV {
 | 
			
		||||
		type integer
 | 
			
		||||
		default -1
 | 
			
		||||
	}		
 | 
			
		||||
| 
						 | 
				
			
			@ -32,16 +46,14 @@ pcm.hw {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pcm.plughw {
 | 
			
		||||
	$.0 CARD
 | 
			
		||||
	$.1 DEV
 | 
			
		||||
	$.2 SUBDEV
 | 
			
		||||
	$.CARD {
 | 
			
		||||
	args [ CARD DEV SUBDEV ]
 | 
			
		||||
	args.CARD {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	$.DEV {
 | 
			
		||||
	args.DEV {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	$.SUBDEV {
 | 
			
		||||
	args.SUBDEV {
 | 
			
		||||
		type integer
 | 
			
		||||
		default -1
 | 
			
		||||
	}		
 | 
			
		||||
| 
						 | 
				
			
			@ -55,8 +67,8 @@ pcm.plughw {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pcm.plug {
 | 
			
		||||
	$.0 SLAVE
 | 
			
		||||
	$.SLAVE {
 | 
			
		||||
	args [ SLAVE ]
 | 
			
		||||
	args.SLAVE {
 | 
			
		||||
		type string
 | 
			
		||||
	}
 | 
			
		||||
	type plug
 | 
			
		||||
| 
						 | 
				
			
			@ -64,12 +76,11 @@ pcm.plug {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pcm.shm {
 | 
			
		||||
	$.0 SOCKET
 | 
			
		||||
	$.1 PCM
 | 
			
		||||
	$.SOCKET {
 | 
			
		||||
	args [ SOCKET PCM ]
 | 
			
		||||
	args.SOCKET {
 | 
			
		||||
		type string
 | 
			
		||||
	}
 | 
			
		||||
	$.PCM {
 | 
			
		||||
	args.PCM {
 | 
			
		||||
		type string
 | 
			
		||||
	}
 | 
			
		||||
	type shm
 | 
			
		||||
| 
						 | 
				
			
			@ -78,16 +89,14 @@ pcm.shm {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pcm.tee {
 | 
			
		||||
	$.0 SLAVE
 | 
			
		||||
	$.1 FILE
 | 
			
		||||
	$.2 FORMAT
 | 
			
		||||
	$.SLAVE {
 | 
			
		||||
	args [ SLAVE FILE FORMAT ]
 | 
			
		||||
	args.SLAVE {
 | 
			
		||||
		type string
 | 
			
		||||
	}
 | 
			
		||||
	$.FILE {
 | 
			
		||||
	args.FILE {
 | 
			
		||||
		type string
 | 
			
		||||
	}
 | 
			
		||||
	$.FORMAT {
 | 
			
		||||
	args.FORMAT {
 | 
			
		||||
		type string
 | 
			
		||||
		default raw
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -98,12 +107,11 @@ pcm.tee {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pcm.file {
 | 
			
		||||
	$.0 FILE
 | 
			
		||||
	$.1 FORMAT
 | 
			
		||||
	$.FILE {
 | 
			
		||||
	args [ FILE FORMAT ]
 | 
			
		||||
	args.FILE {
 | 
			
		||||
		type string
 | 
			
		||||
	}
 | 
			
		||||
	$.FORMAT {
 | 
			
		||||
	args.FORMAT {
 | 
			
		||||
		type string
 | 
			
		||||
		default raw
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -114,47 +122,77 @@ pcm.file {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pcm.surround40 {
 | 
			
		||||
	$.0 CARD
 | 
			
		||||
	$.1 DEV
 | 
			
		||||
	$.CARD {
 | 
			
		||||
	args [ CARD DEV ]
 | 
			
		||||
	args.CARD {
 | 
			
		||||
		type integer
 | 
			
		||||
		default <@ALSA_SURROUND40_CARD,ALSA_PCM_CARD,ALSA_CARD:0@>
 | 
			
		||||
		default {
 | 
			
		||||
			@func getenv
 | 
			
		||||
			@type integer
 | 
			
		||||
			envname [
 | 
			
		||||
				ALSA_SURROUND40_CARD
 | 
			
		||||
				ALSA_PCM_CARD
 | 
			
		||||
				ALSA_CARD
 | 
			
		||||
			]
 | 
			
		||||
			default 0
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	$.DEV {
 | 
			
		||||
	args.DEV {
 | 
			
		||||
		type integer
 | 
			
		||||
		default <@ALSA_SURROUND40_DEVICE:0@>
 | 
			
		||||
	}
 | 
			
		||||
	type surround
 | 
			
		||||
	card $(CARD)
 | 
			
		||||
	device $(DEVICE)
 | 
			
		||||
	stype "4.0"
 | 
			
		||||
}
 | 
			
		||||
	
 | 
			
		||||
pcm.surround40_new {
 | 
			
		||||
	$.0 CARD
 | 
			
		||||
	$.1 DEV
 | 
			
		||||
	$.CARD {
 | 
			
		||||
		type integer
 | 
			
		||||
		default <@ALSA_SURROUND40_CARD,ALSA_PCM_CARD,ALSA_CARD:0@>
 | 
			
		||||
	}
 | 
			
		||||
	$.DEV {
 | 
			
		||||
		type integer
 | 
			
		||||
		default <@ALSA_SURROUND40_DEVICE:0@>
 | 
			
		||||
		default {
 | 
			
		||||
			@func getenv
 | 
			
		||||
			@type integer
 | 
			
		||||
			envname [
 | 
			
		||||
				ALSA_SURROUND40_DEVICE
 | 
			
		||||
			]
 | 
			
		||||
			default 0
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	redirect {
 | 
			
		||||
		filename "&(datadir)/cards/&(card_id:$(CARD)).conf"
 | 
			
		||||
		name "pcm.surround40_$(DEV)_&(pcm_id:$(CARD),$(DEV)):$(CARD)"
 | 
			
		||||
		filename {
 | 
			
		||||
			@func concat
 | 
			
		||||
			strings [
 | 
			
		||||
				{
 | 
			
		||||
					@func datadir
 | 
			
		||||
				}
 | 
			
		||||
				"/cards/"
 | 
			
		||||
				{
 | 
			
		||||
					@func card_strtype
 | 
			
		||||
					card $(CARD)
 | 
			
		||||
				}
 | 
			
		||||
				".conf"
 | 
			
		||||
			]
 | 
			
		||||
		}
 | 
			
		||||
		name {
 | 
			
		||||
			@func concat
 | 
			
		||||
			strings [
 | 
			
		||||
				"pcm.surround40_" $(DEV) "_"
 | 
			
		||||
				{
 | 
			
		||||
					@func pcm_id
 | 
			
		||||
					card $(CARD)
 | 
			
		||||
					device 0
 | 
			
		||||
				}
 | 
			
		||||
				":" $(CARD)
 | 
			
		||||
			]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
	
 | 
			
		||||
pcm.surround51 {
 | 
			
		||||
	$.0 CARD
 | 
			
		||||
	$.1 DEV
 | 
			
		||||
	$.CARD {
 | 
			
		||||
	args [ CARD DEV ]
 | 
			
		||||
	args.CARD {
 | 
			
		||||
		type integer
 | 
			
		||||
		default <@ALSA_SURROUND51_CARD,ALSA_PCM_CARD,ALSA_CARD:0@>
 | 
			
		||||
		default {
 | 
			
		||||
			@func getenv
 | 
			
		||||
			@type integer
 | 
			
		||||
			envname [
 | 
			
		||||
				ALSA_SURROUND51_CARD
 | 
			
		||||
				ALSA_PCM_CARD
 | 
			
		||||
				ALSA_CARD
 | 
			
		||||
			]
 | 
			
		||||
			default 0
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	$.DEV {
 | 
			
		||||
	args.DEV {
 | 
			
		||||
		type integer
 | 
			
		||||
		default <@ALSA_SURROUND51_DEVICE:0@>
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -174,8 +212,8 @@ ctl.default {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
ctl.hw {
 | 
			
		||||
	$.0 CARD
 | 
			
		||||
	$.CARD {
 | 
			
		||||
	args[ CARD ]
 | 
			
		||||
	args.CARD {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	type hw
 | 
			
		||||
| 
						 | 
				
			
			@ -183,39 +221,51 @@ ctl.hw {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
ctl.shm {
 | 
			
		||||
	$.0 SOCKET
 | 
			
		||||
	$.1 PCM
 | 
			
		||||
	$.SOCKET {
 | 
			
		||||
	args [ SOCKET CTL ]
 | 
			
		||||
	args.SOCKET {
 | 
			
		||||
		type string
 | 
			
		||||
	}
 | 
			
		||||
	$.PCM {
 | 
			
		||||
	args.CTL {
 | 
			
		||||
		type string
 | 
			
		||||
	}
 | 
			
		||||
	type shm
 | 
			
		||||
	server $(SOCKET)
 | 
			
		||||
	ctl $(PCM)
 | 
			
		||||
	ctl $(CTL)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
rawmidi.default {
 | 
			
		||||
	type hw
 | 
			
		||||
	card <@ALSA_RAWMIDI_CARD,ALSA_CARD:0@>
 | 
			
		||||
	device <@ALSA_RAWMIDI_DEVICE:0@>
 | 
			
		||||
	card {
 | 
			
		||||
		@func getenv
 | 
			
		||||
		@type integer
 | 
			
		||||
		envname [
 | 
			
		||||
			ALSA_RAWMIDI_CARD
 | 
			
		||||
			ALSA_CARD
 | 
			
		||||
		]
 | 
			
		||||
		default 0
 | 
			
		||||
	}
 | 
			
		||||
	device {
 | 
			
		||||
		@func getenv
 | 
			
		||||
		@type integer
 | 
			
		||||
		envname [
 | 
			
		||||
			ALSA_RAWMIDI_DEVICE
 | 
			
		||||
		]
 | 
			
		||||
		default 0
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
rawmidi.hw {
 | 
			
		||||
	$.0 CARD
 | 
			
		||||
	$.1 DEV
 | 
			
		||||
	$.2 SUBDEV
 | 
			
		||||
	$.CARD {
 | 
			
		||||
	args [ CARD DEV SUBDEV ]
 | 
			
		||||
	args.CARD {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	$.DEV {
 | 
			
		||||
	args.DEV {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	$.SUBDEV {
 | 
			
		||||
	args.SUBDEV {
 | 
			
		||||
		type integer
 | 
			
		||||
		default -1
 | 
			
		||||
	}		
 | 
			
		||||
	}
 | 
			
		||||
	type hw
 | 
			
		||||
	card $(CARD)
 | 
			
		||||
	device $(DEV)
 | 
			
		||||
| 
						 | 
				
			
			@ -231,46 +281,44 @@ seq.hw {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pcm.iec958 {
 | 
			
		||||
	$.0 PCM
 | 
			
		||||
        $.1 AES0
 | 
			
		||||
        $.2 AES1
 | 
			
		||||
        $.3 AES2
 | 
			
		||||
        $.4 AES3
 | 
			
		||||
	$.PCM {
 | 
			
		||||
	args [ PCM AES0 AES1 AES2 AES3 ]
 | 
			
		||||
	args.PCM {
 | 
			
		||||
		type string
 | 
			
		||||
		default default
 | 
			
		||||
	}
 | 
			
		||||
	$.AES0 {
 | 
			
		||||
	args.AES0 {
 | 
			
		||||
		type integer
 | 
			
		||||
		# IEC958_AES0_PROFESSIONAL | IEC958_AES0_NONAUDIO |
 | 
			
		||||
		# IEC958_AES0_PRO_EMPHASIS_NONE | IEC958_AES0_PRO_FS_48000
 | 
			
		||||
		default 0x87
 | 
			
		||||
	}
 | 
			
		||||
	$.AES1 {
 | 
			
		||||
	args.AES1 {
 | 
			
		||||
		type integer
 | 
			
		||||
		default 0x00
 | 
			
		||||
	}
 | 
			
		||||
	$.AES2 {
 | 
			
		||||
	args.AES2 {
 | 
			
		||||
		type integer
 | 
			
		||||
		default 0x00
 | 
			
		||||
	}
 | 
			
		||||
	$.AES3 {
 | 
			
		||||
	args.AES3 {
 | 
			
		||||
		type integer
 | 
			
		||||
		default 0x00
 | 
			
		||||
	}
 | 
			
		||||
	type hooks
 | 
			
		||||
	slave.pcm $(PCM)
 | 
			
		||||
	hooks.0 {
 | 
			
		||||
		type ctl_elems
 | 
			
		||||
		args.0 {
 | 
			
		||||
			name "IEC958 Playback PCM Stream"
 | 
			
		||||
			subdevice 0
 | 
			
		||||
			preserve true
 | 
			
		||||
			lock true
 | 
			
		||||
			value.0 $(AES0)
 | 
			
		||||
			value.1 $(AES1)
 | 
			
		||||
			value.2 $(AES2)
 | 
			
		||||
			value.3 $(AES3)
 | 
			
		||||
	hooks [
 | 
			
		||||
		{
 | 
			
		||||
			type ctl_elems
 | 
			
		||||
			args [
 | 
			
		||||
				name "IEC958 Playback PCM Stream"
 | 
			
		||||
				subdevice 0
 | 
			
		||||
				preserve true
 | 
			
		||||
				lock true
 | 
			
		||||
				value.0 $(AES0)
 | 
			
		||||
				value.1 $(AES1)
 | 
			
		||||
				value.2 $(AES2)
 | 
			
		||||
				value.3 $(AES3)
 | 
			
		||||
			]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,16 +3,14 @@
 | 
			
		|||
#
 | 
			
		||||
 | 
			
		||||
pcm.front {
 | 
			
		||||
	$.0 CARD
 | 
			
		||||
	$.1 DEV
 | 
			
		||||
	$.2 SUBDEV
 | 
			
		||||
	$.CARD {
 | 
			
		||||
	args [ CARD DEV SUBDEV ]
 | 
			
		||||
	args.CARD {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	$.DEV {
 | 
			
		||||
	args.DEV {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	$.SUBDEV {
 | 
			
		||||
	args.SUBDEV {
 | 
			
		||||
		type integer
 | 
			
		||||
		default -1
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -28,16 +26,14 @@ pcm_slave.front {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pcm.rear {
 | 
			
		||||
	$.0 CARD
 | 
			
		||||
	$.1 DEV
 | 
			
		||||
	$.2 SUBDEV
 | 
			
		||||
	$.CARD {
 | 
			
		||||
	args [ CARD DEV SUBDEV ]
 | 
			
		||||
	args.CARD {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	$.DEV {
 | 
			
		||||
	args.DEV {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	$.SUBDEV {
 | 
			
		||||
	args.SUBDEV {
 | 
			
		||||
		type integer
 | 
			
		||||
		default -1
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -53,19 +49,29 @@ pcm_slave.rear {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pcm.surround40_0_trident_dx_nx {
 | 
			
		||||
	$.0 CARD
 | 
			
		||||
	$.CARD {
 | 
			
		||||
	args [ CARD ]
 | 
			
		||||
	args.CARD {
 | 
			
		||||
		type integer
 | 
			
		||||
	}
 | 
			
		||||
	type multi
 | 
			
		||||
	slaves {
 | 
			
		||||
		0 "pcm_slave.front:$(CARD),0,-1"
 | 
			
		||||
		1 "pcm_slave.rear:$(CARD),0,-1"
 | 
			
		||||
	}
 | 
			
		||||
	bindings {
 | 
			
		||||
		0 { slave 0 channel 0 }
 | 
			
		||||
		1 { slave 0 channel 1 }
 | 
			
		||||
		2 { slave 1 channel 0 }
 | 
			
		||||
		3 { slave 1 channel 1 }
 | 
			
		||||
	}
 | 
			
		||||
	slaves [
 | 
			
		||||
		{
 | 
			
		||||
			@func concat
 | 
			
		||||
			strings [
 | 
			
		||||
				"pcm_slave.front:" $(CARD) ",0,-1"
 | 
			
		||||
			]
 | 
			
		||||
		}
 | 
			
		||||
		{
 | 
			
		||||
			@func concat
 | 
			
		||||
			strings [
 | 
			
		||||
				"pcm_slave.rear:" $(CARD) ",0,-1"
 | 
			
		||||
			]
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
	bindings [
 | 
			
		||||
		{ slave 0 channel 0 }
 | 
			
		||||
		{ slave 0 channel 1 }
 | 
			
		||||
		{ slave 1 channel 0 }
 | 
			
		||||
		{ slave 1 channel 1 }
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										748
									
								
								src/conf.c
									
										
									
									
									
								
							
							
						
						
									
										748
									
								
								src/conf.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Configuration helper functions
 | 
			
		||||
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
 | 
			
		||||
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>,
 | 
			
		||||
 *			  Jaroslav Kysela <perex@suse.cz>
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *   This library is free software; you can redistribute it and/or modify
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +23,7 @@
 | 
			
		|||
#include <stdarg.h>
 | 
			
		||||
#include <wordexp.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <dlfcn.h>
 | 
			
		||||
#include "local.h"
 | 
			
		||||
#include "list.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -151,44 +153,9 @@ static int get_char_skip_comments(input_t *input)
 | 
			
		|||
			int err = get_delimstring(&str, '>', input);
 | 
			
		||||
			if (err < 0)
 | 
			
		||||
				return err;
 | 
			
		||||
			if (strlen(str) < 3 ||
 | 
			
		||||
			    str[0] != '@' ||
 | 
			
		||||
			    str[strlen(str)-1] != '@' ||
 | 
			
		||||
			    strchr(str, ':') == NULL) {
 | 
			
		||||
				err = snd_input_stdio_open(&in, str, "r");
 | 
			
		||||
				if (err < 0)
 | 
			
		||||
					return err;
 | 
			
		||||
			} else {
 | 
			
		||||
				char *envvar = str + 1;
 | 
			
		||||
				char *envdef = strchr(str, ':');
 | 
			
		||||
				char *env, *end;
 | 
			
		||||
				
 | 
			
		||||
				str[strlen(str)-1] = '\0';
 | 
			
		||||
				*envdef++ = '\0';
 | 
			
		||||
				if (*envdef == '\0') {
 | 
			
		||||
					free(str);
 | 
			
		||||
					input->error = BAD_ENV_DEFAULT;
 | 
			
		||||
					return -EINVAL;
 | 
			
		||||
				}
 | 
			
		||||
				while (1) {
 | 
			
		||||
					end = strchr(envvar, ',');
 | 
			
		||||
					if (end)
 | 
			
		||||
						*end = '\0';
 | 
			
		||||
					env = getenv(envvar);
 | 
			
		||||
					if (env != NULL && *env != '\0')
 | 
			
		||||
						break;
 | 
			
		||||
					if (end) {
 | 
			
		||||
						*end = ',';	/* repair for fd->name */
 | 
			
		||||
						envvar = end + 1;
 | 
			
		||||
					} else {
 | 
			
		||||
						env = envdef;
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				err = snd_input_buffer_open(&in, env, strlen(env));
 | 
			
		||||
				if (err < 0)
 | 
			
		||||
					return err;
 | 
			
		||||
			}
 | 
			
		||||
			err = snd_input_stdio_open(&in, str, "r");
 | 
			
		||||
			if (err < 0)
 | 
			
		||||
				return err;
 | 
			
		||||
			fd = malloc(sizeof(*fd));
 | 
			
		||||
			if (!fd)
 | 
			
		||||
				return -ENOMEM;
 | 
			
		||||
| 
						 | 
				
			
			@ -272,7 +239,7 @@ static int get_quotedchar(input_t *input)
 | 
			
		|||
 | 
			
		||||
static int get_freestring(char **string, int id, input_t *input)
 | 
			
		||||
{
 | 
			
		||||
	const size_t bufsize = 256;
 | 
			
		||||
	const size_t bufsize = 64;
 | 
			
		||||
	char _buf[bufsize];
 | 
			
		||||
	char *buf = _buf;
 | 
			
		||||
	size_t alloc = bufsize;
 | 
			
		||||
| 
						 | 
				
			
			@ -295,6 +262,8 @@ static int get_freestring(char **string, int id, input_t *input)
 | 
			
		|||
		case ';':
 | 
			
		||||
		case '{':
 | 
			
		||||
		case '}':
 | 
			
		||||
		case '[':
 | 
			
		||||
		case ']':
 | 
			
		||||
		case '\'':
 | 
			
		||||
		case '"':
 | 
			
		||||
		case '\\':
 | 
			
		||||
| 
						 | 
				
			
			@ -319,11 +288,17 @@ static int get_freestring(char **string, int id, input_t *input)
 | 
			
		|||
			alloc *= 2;
 | 
			
		||||
			if (old_alloc == bufsize) {
 | 
			
		||||
				buf = malloc(alloc);
 | 
			
		||||
				if (buf == NULL)
 | 
			
		||||
					return -ENOMEM;
 | 
			
		||||
				memcpy(buf, _buf, old_alloc);
 | 
			
		||||
			} else
 | 
			
		||||
				buf = realloc(buf, alloc);
 | 
			
		||||
			if (!buf)
 | 
			
		||||
				return -ENOMEM;
 | 
			
		||||
			} else {
 | 
			
		||||
				char *ptr = realloc(buf, alloc);
 | 
			
		||||
				if (ptr == NULL) {
 | 
			
		||||
					free(buf);
 | 
			
		||||
					return -ENOMEM;
 | 
			
		||||
				}
 | 
			
		||||
				buf = ptr;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		buf[idx++] = c;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -332,7 +307,7 @@ static int get_freestring(char **string, int id, input_t *input)
 | 
			
		|||
			
 | 
			
		||||
static int get_delimstring(char **string, int delim, input_t *input)
 | 
			
		||||
{
 | 
			
		||||
	const size_t bufsize = 256;
 | 
			
		||||
	const size_t bufsize = 64;
 | 
			
		||||
	char _buf[bufsize];
 | 
			
		||||
	char *buf = _buf;
 | 
			
		||||
	size_t alloc = bufsize;
 | 
			
		||||
| 
						 | 
				
			
			@ -369,11 +344,17 @@ static int get_delimstring(char **string, int delim, input_t *input)
 | 
			
		|||
			alloc *= 2;
 | 
			
		||||
			if (old_alloc == bufsize) {
 | 
			
		||||
				buf = malloc(alloc);
 | 
			
		||||
				if (buf == NULL)
 | 
			
		||||
					return -ENOMEM;
 | 
			
		||||
				memcpy(buf, _buf, old_alloc);
 | 
			
		||||
			} else
 | 
			
		||||
				buf = realloc(buf, alloc);
 | 
			
		||||
			if (!buf)
 | 
			
		||||
				return -ENOMEM;
 | 
			
		||||
			} else {
 | 
			
		||||
				char *ptr = realloc(buf, alloc);
 | 
			
		||||
				if (ptr == NULL) {
 | 
			
		||||
					free(buf);
 | 
			
		||||
					return -ENOMEM;
 | 
			
		||||
				}
 | 
			
		||||
				buf = ptr;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		buf[idx++] = c;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -394,6 +375,8 @@ static int get_string(char **string, int id, input_t *input)
 | 
			
		|||
	case '.':
 | 
			
		||||
	case '{':
 | 
			
		||||
	case '}':
 | 
			
		||||
	case '[':
 | 
			
		||||
	case ']':
 | 
			
		||||
		input->error = UNEXPECTED_CHAR;
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	case '\'':
 | 
			
		||||
| 
						 | 
				
			
			@ -411,17 +394,21 @@ static int get_string(char **string, int id, input_t *input)
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _snd_config_make(snd_config_t **config, char *id,
 | 
			
		||||
			    snd_config_type_t type)
 | 
			
		||||
static int _snd_config_make(snd_config_t **config, char **id, snd_config_type_t type)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_t *n;
 | 
			
		||||
	n = calloc(1, sizeof(*n));
 | 
			
		||||
	if (n == NULL) {
 | 
			
		||||
		if (id)
 | 
			
		||||
			free(id);
 | 
			
		||||
		if (*id) {
 | 
			
		||||
			free(*id);
 | 
			
		||||
			*id = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
	n->id = id;
 | 
			
		||||
	if (id) {
 | 
			
		||||
		n->id = *id;
 | 
			
		||||
		*id = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	n->type = type;
 | 
			
		||||
	if (type == SND_CONFIG_TYPE_COMPOUND)
 | 
			
		||||
		INIT_LIST_HEAD(&n->u.compound.fields);
 | 
			
		||||
| 
						 | 
				
			
			@ -430,7 +417,7 @@ static int _snd_config_make(snd_config_t **config, char *id,
 | 
			
		|||
}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
static int _snd_config_make_add(snd_config_t **config, char *id,
 | 
			
		||||
static int _snd_config_make_add(snd_config_t **config, char **id,
 | 
			
		||||
				snd_config_type_t type, snd_config_t *father)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_t *n;
 | 
			
		||||
| 
						 | 
				
			
			@ -457,38 +444,182 @@ static int _snd_config_search(snd_config_t *config,
 | 
			
		|||
		} else if (strlen(n->id) != (size_t) len ||
 | 
			
		||||
			   memcmp(n->id, id, (size_t) len) != 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		*result = n;
 | 
			
		||||
		if (result)
 | 
			
		||||
			*result = n;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return -ENOENT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_value(snd_config_t **_n, snd_config_t *father, input_t *input, char **id)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_t *n = *_n;
 | 
			
		||||
	char *s;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	err = get_string(&s, 0, input);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	if ((s[0] >= '0' && s[0] <= '9') || s[0] == '-') {
 | 
			
		||||
		long i;
 | 
			
		||||
		errno = 0;
 | 
			
		||||
		err = safe_strtol(s, &i);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			double r;
 | 
			
		||||
			err = safe_strtod(s, &r);
 | 
			
		||||
			if (err >= 0) {
 | 
			
		||||
				free(s);
 | 
			
		||||
				if (n) {
 | 
			
		||||
					if (n->type != SND_CONFIG_TYPE_REAL) {
 | 
			
		||||
						SNDERR("%s is not a real", id);
 | 
			
		||||
						return -EINVAL;
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_REAL, father);
 | 
			
		||||
					if (err < 0)
 | 
			
		||||
						return err;
 | 
			
		||||
				}
 | 
			
		||||
				n->u.real = r;
 | 
			
		||||
				*_n = n;
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			free(s);
 | 
			
		||||
			if (n) {
 | 
			
		||||
				if (n->type != SND_CONFIG_TYPE_INTEGER) {
 | 
			
		||||
					SNDERR("%s is not an integer", id);
 | 
			
		||||
					return -EINVAL;
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_INTEGER, father);
 | 
			
		||||
				if (err < 0)
 | 
			
		||||
					return err;
 | 
			
		||||
			}
 | 
			
		||||
			n->u.integer = i;
 | 
			
		||||
			*_n = n;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (n) {
 | 
			
		||||
		if (n->type != SND_CONFIG_TYPE_STRING) {
 | 
			
		||||
			SNDERR("%s is not a string", id);
 | 
			
		||||
			free(s);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_STRING, father);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
	}
 | 
			
		||||
	if (n->u.string)
 | 
			
		||||
		free(n->u.string);
 | 
			
		||||
	n->u.string = s;
 | 
			
		||||
	*_n = n;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_defs(snd_config_t *father, input_t *input);
 | 
			
		||||
static int parse_array_defs(snd_config_t *farther, input_t *input);
 | 
			
		||||
 | 
			
		||||
static int parse_array_def(snd_config_t *father, input_t *input, int idx)
 | 
			
		||||
{
 | 
			
		||||
	char static_id[12], *id;
 | 
			
		||||
	int c;
 | 
			
		||||
	int err;
 | 
			
		||||
	snd_config_t *n = NULL;
 | 
			
		||||
 | 
			
		||||
	snprintf(static_id, sizeof(static_id), "%i", idx);
 | 
			
		||||
	static_id[sizeof(static_id)-1] = '\0';
 | 
			
		||||
	id = strdup(static_id);
 | 
			
		||||
	if (id == NULL)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	c = get_nonwhite(input);
 | 
			
		||||
	switch (c) {
 | 
			
		||||
	case '{':
 | 
			
		||||
	case '[':
 | 
			
		||||
	{
 | 
			
		||||
		char endchr;
 | 
			
		||||
		if (n) {
 | 
			
		||||
			if (n->type != SND_CONFIG_TYPE_COMPOUND) {
 | 
			
		||||
				SNDERR("%s is not a compound", id);
 | 
			
		||||
				err = -EINVAL;
 | 
			
		||||
				goto __end;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
 | 
			
		||||
			if (err < 0)
 | 
			
		||||
				goto __end;
 | 
			
		||||
		}
 | 
			
		||||
		if (c == '{') {
 | 
			
		||||
			err = parse_defs(n, input);
 | 
			
		||||
			endchr = '}';
 | 
			
		||||
		} else {
 | 
			
		||||
			err = parse_array_defs(n, input);
 | 
			
		||||
			endchr = ']';
 | 
			
		||||
		}
 | 
			
		||||
		c = get_nonwhite(input);
 | 
			
		||||
		if (c != endchr) {
 | 
			
		||||
			snd_config_delete(n);
 | 
			
		||||
			input->error = (c == EOF ? UNEXPECTED_EOF : UNEXPECTED_CHAR);
 | 
			
		||||
			err = -EINVAL;
 | 
			
		||||
			goto __end;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	default:
 | 
			
		||||
		unget_char(c, input);
 | 
			
		||||
		err = parse_value(&n, father, input, &id);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			goto __end;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	err = 0;
 | 
			
		||||
      __end:
 | 
			
		||||
      	if (id)
 | 
			
		||||
      		free(id);
 | 
			
		||||
      	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_array_defs(snd_config_t *father, input_t *input)
 | 
			
		||||
{
 | 
			
		||||
	int idx = 0;
 | 
			
		||||
	while (1) {
 | 
			
		||||
		int c = get_nonwhite(input);
 | 
			
		||||
		int err;
 | 
			
		||||
		if (c == EOF)
 | 
			
		||||
			return 0;
 | 
			
		||||
		unget_char(c, input);
 | 
			
		||||
		if (c == ']')
 | 
			
		||||
			return 0;
 | 
			
		||||
		err = parse_array_def(father, input, idx++);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_def(snd_config_t *father, input_t *input)
 | 
			
		||||
{
 | 
			
		||||
	char *id;
 | 
			
		||||
	char *id = NULL;
 | 
			
		||||
	int c;
 | 
			
		||||
	int err;
 | 
			
		||||
	snd_config_t *n;
 | 
			
		||||
	enum {MERGE, NOCREATE, REMOVE} mode;
 | 
			
		||||
	while (1) {
 | 
			
		||||
#if 0
 | 
			
		||||
		c = get_nonwhite(input);
 | 
			
		||||
		switch (c) {
 | 
			
		||||
#if 0
 | 
			
		||||
		case '?':
 | 
			
		||||
			mode = NOCREATE;
 | 
			
		||||
			break;
 | 
			
		||||
		case '!':
 | 
			
		||||
			mode = REMOVE;
 | 
			
		||||
			break;
 | 
			
		||||
#endif
 | 
			
		||||
		default:
 | 
			
		||||
			mode = MERGE;
 | 
			
		||||
			unget_char(c, input);
 | 
			
		||||
		}
 | 
			
		||||
#else
 | 
			
		||||
		mode = MERGE;
 | 
			
		||||
#endif
 | 
			
		||||
		err = get_string(&id, 1, input);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -510,12 +641,12 @@ static int parse_def(snd_config_t *father, input_t *input)
 | 
			
		|||
		}
 | 
			
		||||
		if (mode == NOCREATE) {
 | 
			
		||||
			SNDERR("%s does not exists", id);
 | 
			
		||||
			free(id);
 | 
			
		||||
			return -ENOENT;
 | 
			
		||||
			err = -ENOENT;
 | 
			
		||||
			goto __end;
 | 
			
		||||
		}
 | 
			
		||||
		err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_COMPOUND, father);
 | 
			
		||||
		err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
			goto __end;
 | 
			
		||||
		n->u.compound.join = 1;
 | 
			
		||||
		father = n;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -526,104 +657,52 @@ static int parse_def(snd_config_t *father, input_t *input)
 | 
			
		|||
			snd_config_delete(n);
 | 
			
		||||
			n = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			free(id);
 | 
			
		||||
	} else {
 | 
			
		||||
		n = NULL;
 | 
			
		||||
		if (mode == NOCREATE) {
 | 
			
		||||
			SNDERR("%s does not exists", id);
 | 
			
		||||
			free(id);
 | 
			
		||||
			return -ENOENT;
 | 
			
		||||
			err = -ENOENT;
 | 
			
		||||
			goto __end;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	switch (c) {
 | 
			
		||||
	case '{':
 | 
			
		||||
	case '[':
 | 
			
		||||
	{
 | 
			
		||||
		char endchr;
 | 
			
		||||
		if (n) {
 | 
			
		||||
			if (n->type != SND_CONFIG_TYPE_COMPOUND) {
 | 
			
		||||
				SNDERR("%s is not a compound", id);
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
				err = -EINVAL;
 | 
			
		||||
				goto __end;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_COMPOUND, father);
 | 
			
		||||
			err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
 | 
			
		||||
			if (err < 0)
 | 
			
		||||
				return err;
 | 
			
		||||
				goto __end;
 | 
			
		||||
		}
 | 
			
		||||
		err = parse_defs(n, input);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			snd_config_delete(n);
 | 
			
		||||
			return err;
 | 
			
		||||
		if (c == '{') {
 | 
			
		||||
			err = parse_defs(n, input);
 | 
			
		||||
			endchr = '}';
 | 
			
		||||
		} else {
 | 
			
		||||
			err = parse_array_defs(n, input);
 | 
			
		||||
			endchr = ']';
 | 
			
		||||
		}
 | 
			
		||||
		c = get_nonwhite(input);
 | 
			
		||||
		if (c != '}') {
 | 
			
		||||
		if (c != endchr) {
 | 
			
		||||
			snd_config_delete(n);
 | 
			
		||||
			input->error = (c == EOF ? UNEXPECTED_EOF : UNEXPECTED_CHAR);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
			err = -EINVAL;
 | 
			
		||||
			goto __end;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	default:
 | 
			
		||||
	{
 | 
			
		||||
		char *s;
 | 
			
		||||
		unget_char(c, input);
 | 
			
		||||
		err = get_string(&s, 0, input);
 | 
			
		||||
		err = parse_value(&n, father, input, &id);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
		if (!err && ((s[0] >= '0' && s[0] <= '9') || s[0] == '-')) {
 | 
			
		||||
			char *ptr;
 | 
			
		||||
			long i;
 | 
			
		||||
			errno = 0;
 | 
			
		||||
			i = strtol(s, &ptr, 0);
 | 
			
		||||
			if (*ptr == '.' || errno != 0) {
 | 
			
		||||
				double r;
 | 
			
		||||
				errno = 0;
 | 
			
		||||
				r = strtod(s, &ptr);
 | 
			
		||||
				if (errno == 0) {
 | 
			
		||||
					free(s);
 | 
			
		||||
					if (n) {
 | 
			
		||||
						if (n->type != SND_CONFIG_TYPE_REAL) {
 | 
			
		||||
							SNDERR("%s is not a real", id);
 | 
			
		||||
							return -EINVAL;
 | 
			
		||||
						}
 | 
			
		||||
					} else {
 | 
			
		||||
						err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_REAL, father);
 | 
			
		||||
						if (err < 0)
 | 
			
		||||
							return err;
 | 
			
		||||
					}
 | 
			
		||||
					n->u.real = r;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			} else if (*ptr == '\0') {
 | 
			
		||||
				free(s);
 | 
			
		||||
				if (n) {
 | 
			
		||||
					if (n->type != SND_CONFIG_TYPE_INTEGER) {
 | 
			
		||||
						SNDERR("%s is not an integer", id);
 | 
			
		||||
						return -EINVAL;
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_INTEGER, father);
 | 
			
		||||
					if (err < 0)
 | 
			
		||||
						return err;
 | 
			
		||||
				}
 | 
			
		||||
				n->u.integer = i;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (n) {
 | 
			
		||||
			if (n->type != SND_CONFIG_TYPE_STRING) {
 | 
			
		||||
				SNDERR("%s is not a string", id);
 | 
			
		||||
				free(s);
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_STRING, father);
 | 
			
		||||
			if (err < 0)
 | 
			
		||||
				return err;
 | 
			
		||||
		}
 | 
			
		||||
		if (n->u.string)
 | 
			
		||||
			free(n->u.string);
 | 
			
		||||
		n->u.string = s;
 | 
			
		||||
	}
 | 
			
		||||
			goto __end;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	c = get_nonwhite(input);
 | 
			
		||||
	switch (c) {
 | 
			
		||||
| 
						 | 
				
			
			@ -633,6 +712,9 @@ static int parse_def(snd_config_t *father, input_t *input)
 | 
			
		|||
	default:
 | 
			
		||||
		unget_char(c, input);
 | 
			
		||||
	}
 | 
			
		||||
      __end:
 | 
			
		||||
      	if (id)
 | 
			
		||||
	      	free(id);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
		
 | 
			
		||||
| 
						 | 
				
			
			@ -820,6 +902,33 @@ static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsi
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Return type of a config node from an ASCII string
 | 
			
		||||
 * \param config Config node handle
 | 
			
		||||
 * \return node type
 | 
			
		||||
 */
 | 
			
		||||
int snd_config_get_type_ascii(const char *ascii, snd_config_type_t *type)
 | 
			
		||||
{
 | 
			
		||||
	assert(ascii && type);
 | 
			
		||||
	if (!strcmp(ascii, "integer")) {
 | 
			
		||||
		*type = SND_CONFIG_TYPE_INTEGER;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (!strcmp(ascii, "real")) {
 | 
			
		||||
		*type = SND_CONFIG_TYPE_REAL;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (!strcmp(ascii, "string")) {
 | 
			
		||||
		*type = SND_CONFIG_TYPE_STRING;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (!strcmp(ascii, "compound")) {
 | 
			
		||||
		*type = SND_CONFIG_TYPE_COMPOUND;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Return type of a config node
 | 
			
		||||
 * \param config Config node handle
 | 
			
		||||
| 
						 | 
				
			
			@ -1012,7 +1121,7 @@ int snd_config_make(snd_config_t **config, const char *id,
 | 
			
		|||
			return -ENOMEM;
 | 
			
		||||
	} else
 | 
			
		||||
		id1 = NULL;
 | 
			
		||||
	return _snd_config_make(config, id1, type);
 | 
			
		||||
	return _snd_config_make(config, &id1, type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -1233,6 +1342,15 @@ int snd_config_get_ascii(snd_config_t *config, char **ascii)
 | 
			
		|||
				assert(0);
 | 
			
		||||
				return -ENOMEM;
 | 
			
		||||
			}
 | 
			
		||||
			if (res[0]) {		/* trim the string */
 | 
			
		||||
				char *ptr;
 | 
			
		||||
				ptr = res + strlen(res) - 1;
 | 
			
		||||
				while (ptr != res && *ptr == ' ')
 | 
			
		||||
					ptr--;
 | 
			
		||||
				if (*ptr != ' ')
 | 
			
		||||
					ptr++;
 | 
			
		||||
				*ptr = '\0';
 | 
			
		||||
			}
 | 
			
		||||
			*ascii = strdup(res);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
| 
						 | 
				
			
			@ -1268,7 +1386,7 @@ int snd_config_save(snd_config_t *config, snd_output_t *out)
 | 
			
		|||
 */
 | 
			
		||||
int snd_config_search(snd_config_t *config, const char *key, snd_config_t **result)
 | 
			
		||||
{
 | 
			
		||||
	assert(config && key && result);
 | 
			
		||||
	assert(config && key);
 | 
			
		||||
	while (1) {
 | 
			
		||||
		snd_config_t *n;
 | 
			
		||||
		int err;
 | 
			
		||||
| 
						 | 
				
			
			@ -1544,7 +1662,8 @@ typedef enum _snd_config_walk_pass {
 | 
			
		|||
} snd_config_walk_pass_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Return 1 if node need to be attached to father */
 | 
			
		||||
/* Return 1 if node needs to be attached to father */
 | 
			
		||||
/* Return 2 if compound is replaced with standard node */
 | 
			
		||||
typedef int (*snd_config_walk_callback_t)(snd_config_t *src,
 | 
			
		||||
					  snd_config_t **dst,
 | 
			
		||||
					  snd_config_walk_pass_t pass,
 | 
			
		||||
| 
						 | 
				
			
			@ -1656,86 +1775,6 @@ int snd_config_copy(snd_config_t **dst,
 | 
			
		|||
	return snd_config_walk(src, dst, _snd_config_copy, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Expand the dynamic contents
 | 
			
		||||
 * \param src Source string
 | 
			
		||||
 * \param idchr Identification character
 | 
			
		||||
 * \param callback Callback function
 | 
			
		||||
 * \param private_data Private data for the given callback function
 | 
			
		||||
 * \param dst Destination string
 | 
			
		||||
 */
 | 
			
		||||
int snd_config_string_replace(const char *src, char idchr,
 | 
			
		||||
			      snd_config_string_replace_callback_t *callback,
 | 
			
		||||
			      void *private_data,
 | 
			
		||||
			      char **dst)
 | 
			
		||||
{
 | 
			
		||||
	int len = 0, len1, err;
 | 
			
		||||
	const char *ptr, *end;
 | 
			
		||||
	char *tmp, *what, *fptr, *rdst = NULL;
 | 
			
		||||
 | 
			
		||||
	assert(src && idchr && dst);
 | 
			
		||||
	while (*src != '\0') {
 | 
			
		||||
		ptr = strchr(src, idchr);
 | 
			
		||||
		end = NULL;
 | 
			
		||||
		if (ptr == src && *(ptr + 1) == '(' && (end = strchr(ptr + 2, ')')) != NULL) {
 | 
			
		||||
			src = end + 1;
 | 
			
		||||
			if (callback == NULL)
 | 
			
		||||
				continue;
 | 
			
		||||
			len1 = end - (ptr + 2);
 | 
			
		||||
			if (len1 == 0)		/* empty */
 | 
			
		||||
				continue;
 | 
			
		||||
			what = malloc(len1 + 1);
 | 
			
		||||
			memcpy(what, ptr + 2, len1);
 | 
			
		||||
			what[len1] = '\0';
 | 
			
		||||
			fptr = NULL;
 | 
			
		||||
			err = callback(what, &fptr, private_data);
 | 
			
		||||
			free(what);
 | 
			
		||||
			if (err < 0) {
 | 
			
		||||
				if (*dst != NULL)
 | 
			
		||||
					free(*dst);
 | 
			
		||||
				return err;
 | 
			
		||||
			}
 | 
			
		||||
			if (fptr == NULL)	/* empty */
 | 
			
		||||
				continue;
 | 
			
		||||
			len1 = strlen(ptr = fptr);
 | 
			
		||||
		} else {
 | 
			
		||||
			if (ptr == NULL) {
 | 
			
		||||
				len1 = strlen(ptr = src);
 | 
			
		||||
			} else {
 | 
			
		||||
				len1 = ptr - src;
 | 
			
		||||
				ptr = src;
 | 
			
		||||
			}
 | 
			
		||||
			src += len1;
 | 
			
		||||
			fptr = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		tmp = realloc(rdst, len + len1 + 1);
 | 
			
		||||
		if (tmp == NULL) {
 | 
			
		||||
			if (*dst != NULL)
 | 
			
		||||
				free(*dst);
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		}
 | 
			
		||||
		memcpy(tmp + len, ptr, len1);
 | 
			
		||||
		tmp[len+=len1] = '\0';
 | 
			
		||||
		if (fptr)
 | 
			
		||||
			free(fptr);
 | 
			
		||||
		rdst = tmp;
 | 
			
		||||
	}
 | 
			
		||||
	*dst = rdst;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _snd_config_expand_replace(const char *what, char **dst, void *private_data)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_t *vars = private_data;
 | 
			
		||||
	snd_config_t *val;
 | 
			
		||||
 | 
			
		||||
	assert(dst);
 | 
			
		||||
	if (snd_config_search(vars, what, &val) < 0)
 | 
			
		||||
		return 0;	/* empty */
 | 
			
		||||
	else
 | 
			
		||||
		return snd_config_get_ascii(val, dst);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _snd_config_expand(snd_config_t *src,
 | 
			
		||||
			      snd_config_t **dst,
 | 
			
		||||
			      snd_config_walk_pass_t pass,
 | 
			
		||||
| 
						 | 
				
			
			@ -1746,12 +1785,14 @@ static int _snd_config_expand(snd_config_t *src,
 | 
			
		|||
	snd_config_type_t type = snd_config_get_type(src);
 | 
			
		||||
	switch (pass) {
 | 
			
		||||
	case SND_CONFIG_WALK_PASS_PRE:
 | 
			
		||||
		if (strcmp(id, "$") == 0)
 | 
			
		||||
	{
 | 
			
		||||
		if (strcmp(id, "args") == 0)
 | 
			
		||||
			return 0;
 | 
			
		||||
		err = snd_config_make_compound(dst, id, src->u.compound.join);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	case SND_CONFIG_WALK_PASS_LEAF:
 | 
			
		||||
		switch (type) {
 | 
			
		||||
		case SND_CONFIG_TYPE_INTEGER:
 | 
			
		||||
| 
						 | 
				
			
			@ -1781,11 +1822,12 @@ static int _snd_config_expand(snd_config_t *src,
 | 
			
		|||
			const char *s;
 | 
			
		||||
			snd_config_t *val;
 | 
			
		||||
			snd_config_t *vars = private_data;
 | 
			
		||||
			err = snd_config_get_string(src, &s);
 | 
			
		||||
			if (strncmp(s, "$(", 2) == 0 && s[strlen(s) - 1] == ')') {
 | 
			
		||||
				char *str = alloca((strlen(s) - 3) + 1);
 | 
			
		||||
				memcpy(str, s + 2, strlen(s) - 3);
 | 
			
		||||
				str[strlen(s) - 3] = 0;
 | 
			
		||||
			snd_config_get_string(src, &s);
 | 
			
		||||
                        if (strncmp(s, "$(", 2) == 0 && s[strlen(s) - 1] == ')') {
 | 
			
		||||
                        	int len = strlen(s) - 3;
 | 
			
		||||
				char *str = alloca(len + 1);
 | 
			
		||||
				memcpy(str, s + 2, len);
 | 
			
		||||
				str[len] = '\0';
 | 
			
		||||
				if (snd_config_search(vars, str, &val) < 0)
 | 
			
		||||
					return 0;
 | 
			
		||||
				err = snd_config_copy(dst, val);
 | 
			
		||||
| 
						 | 
				
			
			@ -1800,22 +1842,10 @@ static int _snd_config_expand(snd_config_t *src,
 | 
			
		|||
				err = snd_config_make(dst, id, type);
 | 
			
		||||
				if (err < 0)
 | 
			
		||||
					return err;
 | 
			
		||||
				if (strstr(s, "$(") != NULL) {
 | 
			
		||||
					char *str = NULL;
 | 
			
		||||
					err = snd_config_string_replace(s, '$', _snd_config_expand_replace, vars, &str);
 | 
			
		||||
					if (err < 0)
 | 
			
		||||
						return err;
 | 
			
		||||
					if (str == NULL) {
 | 
			
		||||
						snd_config_delete(*dst);
 | 
			
		||||
						return 0;
 | 
			
		||||
					}
 | 
			
		||||
					(*dst)->u.string = str;
 | 
			
		||||
				} else {
 | 
			
		||||
					err = snd_config_set_string(*dst, s);
 | 
			
		||||
					if (err < 0) {
 | 
			
		||||
						snd_config_delete(*dst);
 | 
			
		||||
						return err;
 | 
			
		||||
					}
 | 
			
		||||
				err = snd_config_set_string(*dst, s);
 | 
			
		||||
				if (err < 0) {
 | 
			
		||||
					snd_config_delete(*dst);
 | 
			
		||||
					return err;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
| 
						 | 
				
			
			@ -1830,6 +1860,181 @@ static int _snd_config_expand(snd_config_t *src,
 | 
			
		|||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int evaluate_node(snd_config_t *father, snd_config_t *src,
 | 
			
		||||
			 void *private_data, snd_config_t **_dst)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_iterator_t i, next;
 | 
			
		||||
	const char *lib = NULL, *func = NULL, *type = NULL;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	assert(father && src && _dst);
 | 
			
		||||
 | 
			
		||||
	snd_config_for_each(i, next, src) {
 | 
			
		||||
		snd_config_t *n = snd_config_iterator_entry(i);
 | 
			
		||||
		const char *id;
 | 
			
		||||
		if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) {
 | 
			
		||||
			snd_config_t *n1;
 | 
			
		||||
			err = evaluate_node(src, n, private_data, &n1);
 | 
			
		||||
			if (err < 0) {
 | 
			
		||||
				SNDERR("Error in node %s", snd_config_get_id(n));
 | 
			
		||||
				goto __error;
 | 
			
		||||
			}
 | 
			
		||||
			if (n1) {			/* replace node */
 | 
			
		||||
				snd_config_delete(n);
 | 
			
		||||
				err = snd_config_add(src, n1);
 | 
			
		||||
				if (err < 0)
 | 
			
		||||
					goto __error;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		id = snd_config_get_id(n);
 | 
			
		||||
		if (*id++ != '@')	/* quick look */
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!strcmp(id, "lib")) {
 | 
			
		||||
			if ((err = snd_config_get_string(n, &lib)) < 0) {
 | 
			
		||||
			       _invalid_field:
 | 
			
		||||
				SNDERR("Unknown type of field %s", id);
 | 
			
		||||
				return err;
 | 
			
		||||
			}
 | 
			
		||||
			lib = strdup(lib);
 | 
			
		||||
			if (lib == NULL)
 | 
			
		||||
				goto __error;
 | 
			
		||||
			snd_config_delete(n);
 | 
			
		||||
		} else if (!strcmp(id, "func")) {
 | 
			
		||||
			if ((err = snd_config_get_string(n, &func)) < 0)
 | 
			
		||||
				goto _invalid_field;
 | 
			
		||||
			func = strdup(func);
 | 
			
		||||
			if (func == NULL)
 | 
			
		||||
				goto __error;
 | 
			
		||||
			snd_config_delete(n);
 | 
			
		||||
		} else if (!strcmp(id, "type")) {
 | 
			
		||||
			if ((err = snd_config_get_string(n, &type)) < 0)
 | 
			
		||||
				goto _invalid_field;
 | 
			
		||||
			type = strdup(type);
 | 
			
		||||
			if (type == NULL)
 | 
			
		||||
				goto __error;
 | 
			
		||||
			snd_config_delete(n);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if (func == NULL) {
 | 
			
		||||
		*_dst = NULL;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		char buf[64], *ptr;
 | 
			
		||||
		snd_config_type_t t;
 | 
			
		||||
		snd_config_t *dst = NULL;
 | 
			
		||||
		char *evaluate_name = NULL;
 | 
			
		||||
		int (*evaluate_func)(char **dst, snd_config_t *src, void *private_data);
 | 
			
		||||
		void *h;
 | 
			
		||||
	
 | 
			
		||||
		if (evaluate_name == NULL) {
 | 
			
		||||
			snprintf(buf, sizeof(buf), "snd_func_%s", func);
 | 
			
		||||
			buf[sizeof(buf)-1] = '\0';
 | 
			
		||||
			evaluate_name = buf;
 | 
			
		||||
		}
 | 
			
		||||
	
 | 
			
		||||
		h = dlopen(lib, RTLD_NOW);
 | 
			
		||||
		if (!h) {
 | 
			
		||||
			SNDERR("Cannot open shared library %s", lib);
 | 
			
		||||
			return -ENOENT;
 | 
			
		||||
		}
 | 
			
		||||
		evaluate_func = dlsym(h, evaluate_name);
 | 
			
		||||
		if (!evaluate_func) {
 | 
			
		||||
			dlclose(h);
 | 
			
		||||
			SNDERR("symbol %s is not defined inside %s", evaluate_name, lib ? lib : ALSA_LIB);
 | 
			
		||||
			return -ENXIO;
 | 
			
		||||
		}
 | 
			
		||||
		err = evaluate_func(&ptr, src, private_data);
 | 
			
		||||
		dlclose(h);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			SNDERR("function %s returned error: %s", evaluate_name, snd_strerror(err));
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
		if (type == NULL) {
 | 
			
		||||
			t = SND_CONFIG_TYPE_STRING;
 | 
			
		||||
		} else {
 | 
			
		||||
			err = snd_config_get_type_ascii(type, &t);
 | 
			
		||||
			if (err < 0 || t == SND_CONFIG_TYPE_COMPOUND) {
 | 
			
		||||
				free(ptr);
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		err = snd_config_make(&dst, snd_config_get_id(src), t);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			free(ptr);
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
		switch (t) {
 | 
			
		||||
		case SND_CONFIG_TYPE_INTEGER:
 | 
			
		||||
			{
 | 
			
		||||
				long v;
 | 
			
		||||
				err = safe_strtol(ptr, &v);
 | 
			
		||||
				if (err < 0) {
 | 
			
		||||
					free(ptr);
 | 
			
		||||
					snd_config_delete(dst);
 | 
			
		||||
					return err;
 | 
			
		||||
				}
 | 
			
		||||
				snd_config_set_integer(dst, v);
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case SND_CONFIG_TYPE_REAL:
 | 
			
		||||
			{
 | 
			
		||||
				double r;
 | 
			
		||||
				err = safe_strtod(ptr, &r);
 | 
			
		||||
				if (err < 0) {
 | 
			
		||||
					free(ptr);
 | 
			
		||||
					snd_config_delete(dst);
 | 
			
		||||
					return err;
 | 
			
		||||
				}
 | 
			
		||||
				snd_config_set_real(dst, r);
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			snd_config_set_string(dst, ptr);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		free(ptr);
 | 
			
		||||
		*_dst = dst;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	err = 0;
 | 
			
		||||
      __error:
 | 
			
		||||
      	if (func)
 | 
			
		||||
      		free((void *)func);
 | 
			
		||||
      	if (lib)
 | 
			
		||||
      		free((void *)lib);
 | 
			
		||||
      	if (type)
 | 
			
		||||
      		free((void *)type);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int snd_config_evaluate(snd_config_t *conf, void *private_data)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_iterator_t i, next;
 | 
			
		||||
 | 
			
		||||
	assert(conf);
 | 
			
		||||
	snd_config_for_each(i, next, conf) {
 | 
			
		||||
		snd_config_t *n = snd_config_iterator_entry(i);
 | 
			
		||||
		if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) {
 | 
			
		||||
			snd_config_t *n1;
 | 
			
		||||
			int err = evaluate_node(conf, n, private_data, &n1);
 | 
			
		||||
			if (err < 0) {
 | 
			
		||||
				SNDERR("Error in node %s", snd_config_get_id(n));
 | 
			
		||||
				return err;
 | 
			
		||||
			}
 | 
			
		||||
			if (n1) {			/* replace node */
 | 
			
		||||
				snd_config_delete(n);
 | 
			
		||||
				err = snd_config_add(conf, n1);
 | 
			
		||||
				if (err < 0)
 | 
			
		||||
					return err;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int load_defaults(snd_config_t *subs, snd_config_t *defs)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_iterator_t d, dnext;
 | 
			
		||||
| 
						 | 
				
			
			@ -2055,6 +2260,8 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
 | 
			
		|||
{
 | 
			
		||||
	int err;
 | 
			
		||||
	int arg = 0;
 | 
			
		||||
	if (str == NULL)
 | 
			
		||||
		return 0;
 | 
			
		||||
	skip_blank(&str);
 | 
			
		||||
	if (!*str)
 | 
			
		||||
		return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -2198,35 +2405,54 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Expand a node applying arguments
 | 
			
		||||
 * \brief Expand a node applying arguments and functions
 | 
			
		||||
 * \param config Config node handle
 | 
			
		||||
 * \param args Arguments string
 | 
			
		||||
 * \param args Arguments string (optional)
 | 
			
		||||
 * \param private_data Private data for functions
 | 
			
		||||
 * \param result Pointer to found node
 | 
			
		||||
 * \return 0 on success otherwise a negative error code
 | 
			
		||||
 */
 | 
			
		||||
int snd_config_expand(snd_config_t *config, const char *args,
 | 
			
		||||
		      snd_config_t **result)
 | 
			
		||||
		      void *private_data, snd_config_t **result)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
	snd_config_t *defs, *subs;
 | 
			
		||||
	err = snd_config_search(config, "$", &defs);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	err = snd_config_top(&subs);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	err = load_defaults(subs, defs);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		goto _end;
 | 
			
		||||
	err = parse_args(subs, args, defs);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		goto _end;
 | 
			
		||||
	err = snd_config_walk(config, result, _snd_config_expand, subs);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
	snd_config_t *defs, *subs = NULL, *res;
 | 
			
		||||
	err = snd_config_search(config, "args", &defs);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		err = snd_config_copy(&res, config);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
	} else {
 | 
			
		||||
		err = snd_config_top(&subs);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
		err = load_defaults(subs, defs);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			SNDERR("Load defaults error: %s", snd_strerror(err));
 | 
			
		||||
			goto _end;
 | 
			
		||||
		}
 | 
			
		||||
		err = parse_args(subs, args, defs);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			SNDERR("Parse arguments error: %s", snd_strerror(err));
 | 
			
		||||
			goto _end;
 | 
			
		||||
		}
 | 
			
		||||
		err = snd_config_walk(config, &res, _snd_config_expand, subs);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			SNDERR("Expand error (walk): %s", snd_strerror(err));
 | 
			
		||||
			goto _end;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	err = snd_config_evaluate(res, private_data);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("Evaluate error: %s", snd_strerror(err));
 | 
			
		||||
		snd_config_delete(res);
 | 
			
		||||
		goto _end;
 | 
			
		||||
	}
 | 
			
		||||
	*result = res;
 | 
			
		||||
	err = 1;
 | 
			
		||||
 _end:
 | 
			
		||||
	snd_config_delete(subs);
 | 
			
		||||
 	if (subs)
 | 
			
		||||
		snd_config_delete(subs);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										469
									
								
								src/confmisc.c
									
										
									
									
									
								
							
							
						
						
									
										469
									
								
								src/confmisc.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -139,88 +139,72 @@ int snd_config_get_ctl_iface(snd_config_t *conf)
 | 
			
		|||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _snd_config_redirect_load_replace(const char *what, char **dst, void *private_data ATTRIBUTE_UNUSED)
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Expand the dynamic contents
 | 
			
		||||
 * \param src Source string
 | 
			
		||||
 * \param idchr Identification character
 | 
			
		||||
 * \param callback Callback function
 | 
			
		||||
 * \param private_data Private data for the given callback function
 | 
			
		||||
 * \param dst Destination string
 | 
			
		||||
 */
 | 
			
		||||
int snd_config_string_replace(const char *src, char idchr,
 | 
			
		||||
			      snd_config_string_replace_callback_t *callback,
 | 
			
		||||
			      void *private_data,
 | 
			
		||||
			      char **dst)
 | 
			
		||||
{
 | 
			
		||||
	enum {
 | 
			
		||||
		CARD_ID,
 | 
			
		||||
		PCM_ID,
 | 
			
		||||
		RAWMIDI_ID
 | 
			
		||||
	} id;
 | 
			
		||||
	int len;
 | 
			
		||||
	int len = 0, len1, err;
 | 
			
		||||
	const char *ptr, *end;
 | 
			
		||||
	char *tmp, *what, *fptr, *rdst = NULL;
 | 
			
		||||
 | 
			
		||||
	if (!strcmp(what, "datadir")) {
 | 
			
		||||
		*dst = strdup(DATADIR "/alsa");
 | 
			
		||||
		return *dst == NULL ? -ENOMEM : 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (!strncmp(what, "card_id:", len = 8))
 | 
			
		||||
		id = CARD_ID;
 | 
			
		||||
	else if (!strncmp(what, "pcm_id:", len = 7))
 | 
			
		||||
		id = PCM_ID;
 | 
			
		||||
	else if (!strncmp(what, "rawmidi_id:", len = 11))
 | 
			
		||||
		id = RAWMIDI_ID;
 | 
			
		||||
	else
 | 
			
		||||
		return 0;
 | 
			
		||||
	{
 | 
			
		||||
		snd_ctl_t *ctl;
 | 
			
		||||
		int err;
 | 
			
		||||
		char name[12];
 | 
			
		||||
		const char *str = NULL;
 | 
			
		||||
		char *fstr = NULL;
 | 
			
		||||
		sprintf(name, "hw:%d", atoi(what + len));
 | 
			
		||||
		err = snd_ctl_open(&ctl, name, 0);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
		switch (id) {
 | 
			
		||||
		case CARD_ID:
 | 
			
		||||
			{
 | 
			
		||||
				snd_ctl_card_info_t *info;
 | 
			
		||||
				snd_ctl_card_info_alloca(&info);
 | 
			
		||||
				err = snd_ctl_card_info(ctl, info);
 | 
			
		||||
				if (err < 0)
 | 
			
		||||
					return err;
 | 
			
		||||
				err = snd_card_type_enum_to_string(snd_ctl_card_info_get_type(info), &fstr);
 | 
			
		||||
	assert(src && idchr && dst);
 | 
			
		||||
	while (*src != '\0') {
 | 
			
		||||
		ptr = strchr(src, idchr);
 | 
			
		||||
		end = NULL;
 | 
			
		||||
		if (ptr == src && *(ptr + 1) == '(' && (end = strchr(ptr + 2, ')')) != NULL) {
 | 
			
		||||
			src = end + 1;
 | 
			
		||||
			if (callback == NULL)
 | 
			
		||||
				continue;
 | 
			
		||||
			len1 = end - (ptr + 2);
 | 
			
		||||
			if (len1 == 0)		/* empty */
 | 
			
		||||
				continue;
 | 
			
		||||
			what = malloc(len1 + 1);
 | 
			
		||||
			memcpy(what, ptr + 2, len1);
 | 
			
		||||
			what[len1] = '\0';
 | 
			
		||||
			fptr = NULL;
 | 
			
		||||
			err = callback(what, &fptr, private_data);
 | 
			
		||||
			free(what);
 | 
			
		||||
			if (err < 0) {
 | 
			
		||||
				if (*dst != NULL)
 | 
			
		||||
					free(*dst);
 | 
			
		||||
				return err;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case PCM_ID:
 | 
			
		||||
			{
 | 
			
		||||
				char *ptr = strchr(what + len, ',');
 | 
			
		||||
				int dev = atoi(what + len);
 | 
			
		||||
				int subdev = ptr ? atoi(ptr + 1) : -1;
 | 
			
		||||
				snd_pcm_info_t *info;
 | 
			
		||||
				snd_pcm_info_alloca(&info);
 | 
			
		||||
				snd_pcm_info_set_device(info, dev);
 | 
			
		||||
				snd_pcm_info_set_subdevice(info, subdev);
 | 
			
		||||
				err = snd_ctl_pcm_info(ctl, info);
 | 
			
		||||
				if (err < 0)
 | 
			
		||||
					return err;
 | 
			
		||||
				str = snd_pcm_info_get_id(info);
 | 
			
		||||
			if (fptr == NULL)	/* empty */
 | 
			
		||||
				continue;
 | 
			
		||||
			len1 = strlen(ptr = fptr);
 | 
			
		||||
		} else {
 | 
			
		||||
			if (ptr == NULL) {
 | 
			
		||||
				len1 = strlen(ptr = src);
 | 
			
		||||
			} else {
 | 
			
		||||
				len1 = ptr - src;
 | 
			
		||||
				ptr = src;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case RAWMIDI_ID:
 | 
			
		||||
			{
 | 
			
		||||
				char *ptr = strchr(what + len, ',');
 | 
			
		||||
				int dev = atoi(what + len);
 | 
			
		||||
				int subdev = ptr ? atoi(ptr + 1) : -1;
 | 
			
		||||
				snd_rawmidi_info_t *info;
 | 
			
		||||
				snd_rawmidi_info_alloca(&info);
 | 
			
		||||
				snd_rawmidi_info_set_device(info, dev);
 | 
			
		||||
				snd_rawmidi_info_set_subdevice(info, subdev);
 | 
			
		||||
				err = snd_ctl_rawmidi_info(ctl, info);
 | 
			
		||||
				if (err < 0)
 | 
			
		||||
					return err;
 | 
			
		||||
				str = snd_rawmidi_info_get_id(info);
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
			src += len1;
 | 
			
		||||
			fptr = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
		snd_ctl_close(ctl);
 | 
			
		||||
		*dst = fstr ? fstr : (str ? strdup(str) : NULL);
 | 
			
		||||
		if (*dst == NULL)
 | 
			
		||||
			return 0;
 | 
			
		||||
		return 0;
 | 
			
		||||
		tmp = realloc(rdst, len + len1 + 1);
 | 
			
		||||
		if (tmp == NULL) {
 | 
			
		||||
			if (*dst != NULL)
 | 
			
		||||
				free(*dst);
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		}
 | 
			
		||||
		memcpy(tmp + len, ptr, len1);
 | 
			
		||||
		tmp[len+=len1] = '\0';
 | 
			
		||||
		if (fptr)
 | 
			
		||||
			free(fptr);
 | 
			
		||||
		rdst = tmp;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;	/* empty */
 | 
			
		||||
	*dst = rdst;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -262,19 +246,14 @@ int snd_config_redirect_load(snd_config_t *root,
 | 
			
		|||
	rname = NULL;
 | 
			
		||||
	if (snd_config_search(config, "filename", &c) >= 0) {
 | 
			
		||||
		snd_config_t *rconfig;
 | 
			
		||||
		char *filename;
 | 
			
		||||
		const char *filename;
 | 
			
		||||
		snd_input_t *input;
 | 
			
		||||
		err = snd_config_copy(&rconfig, root);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
		if (snd_config_get_type(c) == SND_CONFIG_TYPE_STRING) {
 | 
			
		||||
			snd_config_get_string(c, (const char **)&filename);
 | 
			
		||||
			if ((err = snd_config_string_replace(filename, '&', _snd_config_redirect_load_replace, NULL, &filename)) < 0)
 | 
			
		||||
				goto __filename_error;
 | 
			
		||||
			if (filename == NULL)
 | 
			
		||||
				goto __filename_error_einval;
 | 
			
		||||
			snd_config_get_string(c, &filename);
 | 
			
		||||
		} else {
 | 
			
		||||
		      __filename_error_einval:
 | 
			
		||||
			err = -EINVAL;
 | 
			
		||||
		      __filename_error:
 | 
			
		||||
			snd_config_delete(rconfig);
 | 
			
		||||
| 
						 | 
				
			
			@ -289,10 +268,8 @@ int snd_config_redirect_load(snd_config_t *root,
 | 
			
		|||
		if (err < 0) {
 | 
			
		||||
			snd_input_close(input);
 | 
			
		||||
			goto __filename_error;
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
		snd_input_close(input);
 | 
			
		||||
		free(filename);
 | 
			
		||||
		result = rconfig;
 | 
			
		||||
		dynamic = 1;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -300,8 +277,11 @@ int snd_config_redirect_load(snd_config_t *root,
 | 
			
		|||
		const char *ptr;
 | 
			
		||||
		if ((err = snd_config_get_string(c, &ptr)) < 0)
 | 
			
		||||
			goto __error;
 | 
			
		||||
		if ((err = snd_config_string_replace(ptr, '&', _snd_config_redirect_load_replace, NULL, &rname)) < 0)
 | 
			
		||||
		rname = strdup(ptr);
 | 
			
		||||
		if (rname == NULL) {
 | 
			
		||||
			err = -ENOMEM;
 | 
			
		||||
			goto __error;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (rname == NULL) {
 | 
			
		||||
		err = -EINVAL;
 | 
			
		||||
| 
						 | 
				
			
			@ -318,3 +298,320 @@ int snd_config_redirect_load(snd_config_t *root,
 | 
			
		|||
      		snd_config_delete(result);
 | 
			
		||||
      	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  Helper functions for the configuration file
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
int snd_func_getenv(char **dst, snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_t *n, *d, *e;
 | 
			
		||||
	snd_config_iterator_t i, next;
 | 
			
		||||
	const char *res;
 | 
			
		||||
	char *def = NULL;
 | 
			
		||||
	int idx = 0, err;
 | 
			
		||||
	
 | 
			
		||||
	err = snd_config_expand(src, NULL, NULL, &e);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	err = snd_config_search(e, "envname", &n);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("field envname not found");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	err = snd_config_search(e, "default", &d);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("field default not found");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	err = snd_config_get_ascii(d, &def);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("error getting field default");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
      __retry:
 | 
			
		||||
	snd_config_for_each(i, next, n) {
 | 
			
		||||
		snd_config_t *n = snd_config_iterator_entry(i);
 | 
			
		||||
		const char *id = snd_config_get_id(n);
 | 
			
		||||
		const char *ptr, *env;
 | 
			
		||||
		long i;
 | 
			
		||||
		if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
 | 
			
		||||
			SNDERR("field %s is not a string", id);
 | 
			
		||||
			err = -EINVAL;
 | 
			
		||||
			goto __error;
 | 
			
		||||
		}
 | 
			
		||||
		err = safe_strtol(id, &i);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			SNDERR("id of field %s is not an integer", id);
 | 
			
		||||
			err = -EINVAL;
 | 
			
		||||
			goto __error;
 | 
			
		||||
		}
 | 
			
		||||
		if (i == idx) {
 | 
			
		||||
			idx++;
 | 
			
		||||
			snd_config_get_string(n, &ptr);
 | 
			
		||||
			env = getenv(ptr);
 | 
			
		||||
			if (env != NULL && *env != '\0') {
 | 
			
		||||
				res = strdup(env);
 | 
			
		||||
				goto __ok;
 | 
			
		||||
			}
 | 
			
		||||
			goto __retry;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	res = def;
 | 
			
		||||
	def = NULL;
 | 
			
		||||
      __ok:
 | 
			
		||||
	err = res == NULL ? -ENOMEM : 0;
 | 
			
		||||
	*dst = (char *)res;
 | 
			
		||||
      __error:
 | 
			
		||||
      	if (def)
 | 
			
		||||
      		free(def);
 | 
			
		||||
	snd_config_delete(e);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int snd_func_concat(char **dst, snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_t *n, *e;
 | 
			
		||||
	snd_config_iterator_t i, next;
 | 
			
		||||
	char *res = NULL, *tmp;
 | 
			
		||||
	int idx = 0, len = 0, len1, err;
 | 
			
		||||
	
 | 
			
		||||
	err = snd_config_expand(src, NULL, NULL, &e);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	err = snd_config_search(e, "strings", &n);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("field strings not found");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
      __retry:
 | 
			
		||||
	snd_config_for_each(i, next, n) {
 | 
			
		||||
		snd_config_t *n = snd_config_iterator_entry(i);
 | 
			
		||||
		char *ptr;
 | 
			
		||||
		const char *id = snd_config_get_id(n);
 | 
			
		||||
		long i;
 | 
			
		||||
		err = safe_strtol(id, &i);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			SNDERR("id of field %s is not an integer", id);
 | 
			
		||||
			err = -EINVAL;
 | 
			
		||||
			goto __error;
 | 
			
		||||
		}
 | 
			
		||||
		if (i == idx) {
 | 
			
		||||
			idx++;
 | 
			
		||||
			snd_config_get_ascii(n, &ptr);
 | 
			
		||||
			len1 = strlen(ptr);
 | 
			
		||||
			tmp = realloc(res, len + len1 + 1);
 | 
			
		||||
			if (tmp == NULL) {
 | 
			
		||||
				free(ptr);
 | 
			
		||||
				if (res)
 | 
			
		||||
					free(res);
 | 
			
		||||
				err = -ENOMEM;
 | 
			
		||||
				goto __error;
 | 
			
		||||
			}
 | 
			
		||||
			memcpy(tmp + len, ptr, len1);
 | 
			
		||||
			free(ptr);
 | 
			
		||||
			len += len1;
 | 
			
		||||
			tmp[len] = '\0';
 | 
			
		||||
			res = tmp;
 | 
			
		||||
			goto __retry;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (res == NULL) {
 | 
			
		||||
		SNDERR("empty string is not accepted");
 | 
			
		||||
		err = -EINVAL;
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	err = 0;
 | 
			
		||||
	*dst = res;
 | 
			
		||||
      __error:
 | 
			
		||||
	snd_config_delete(e);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int snd_func_datadir(char **dst, snd_config_t *src ATTRIBUTE_UNUSED, void *private_data ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	char *res = strdup(DATADIR "/alsa");
 | 
			
		||||
	if (res == NULL)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	*dst = res;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int open_ctl(long card, snd_ctl_t **ctl)
 | 
			
		||||
{
 | 
			
		||||
	char name[16];
 | 
			
		||||
	snprintf(name, sizeof(name), "hw:%li", card);
 | 
			
		||||
	name[sizeof(name)-1] = '\0';
 | 
			
		||||
	return snd_ctl_open(ctl, name, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
static int string_from_integer(char **dst, long v)
 | 
			
		||||
{
 | 
			
		||||
	char str[32];
 | 
			
		||||
	char *res;
 | 
			
		||||
	sprintf(str, "%li", v);
 | 
			
		||||
	res = strdup(str);
 | 
			
		||||
	if (res == NULL)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	*dst = res;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int snd_func_card_strtype(char **dst, snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_t *n, *e;
 | 
			
		||||
	char *res = NULL;
 | 
			
		||||
	snd_ctl_t *ctl = NULL;
 | 
			
		||||
	snd_ctl_card_info_t *info;
 | 
			
		||||
	long v;
 | 
			
		||||
	int err;
 | 
			
		||||
	
 | 
			
		||||
	err = snd_config_expand(src, NULL, NULL, &e);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	err = snd_config_search(e, "card", &n);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("field card not found");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	err = snd_config_get_integer(n, &v);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("field card is not an integer");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	err = open_ctl(v, &ctl);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("could not open control for card %li", v);
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	snd_ctl_card_info_alloca(&info);
 | 
			
		||||
	err = snd_ctl_card_info(ctl, info);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("snd_ctl_card_info error: %s", snd_strerror(err));
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	err = snd_card_type_enum_to_string(snd_ctl_card_info_get_type(info), &res);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("snd_card_type_enum_to_string failed for %i", (int)snd_ctl_card_info_get_type(info));
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	*dst = res;
 | 
			
		||||
      __error:
 | 
			
		||||
      	if (ctl)
 | 
			
		||||
      		snd_ctl_close(ctl);
 | 
			
		||||
	snd_config_delete(e);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int snd_func_card_id(char **dst, snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_t *n, *e;
 | 
			
		||||
	char *res = NULL;
 | 
			
		||||
	snd_ctl_t *ctl = NULL;
 | 
			
		||||
	snd_ctl_card_info_t *info;
 | 
			
		||||
	long v;
 | 
			
		||||
	int err;
 | 
			
		||||
	
 | 
			
		||||
	err = snd_config_expand(src, NULL, NULL, &e);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	err = snd_config_search(e, "card", &n);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("field card not found");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	err = snd_config_get_integer(n, &v);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("field card is not an integer");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	err = open_ctl(v, &ctl);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("could not open control for card %li", v);
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	snd_ctl_card_info_alloca(&info);
 | 
			
		||||
	err = snd_ctl_card_info(ctl, info);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("snd_ctl_card_info error: %s", snd_strerror(err));
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	res = strdup(snd_ctl_card_info_get_id(info));
 | 
			
		||||
	if (res == NULL) {
 | 
			
		||||
		err = -ENOMEM;
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	*dst = res;
 | 
			
		||||
      __error:
 | 
			
		||||
      	if (ctl)
 | 
			
		||||
      		snd_ctl_close(ctl);
 | 
			
		||||
	snd_config_delete(e);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int snd_func_pcm_id(char **dst, snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_t *n, *e;
 | 
			
		||||
	char *res = NULL;
 | 
			
		||||
	snd_ctl_t *ctl = NULL;
 | 
			
		||||
	snd_pcm_info_t *info;
 | 
			
		||||
	long card, device, subdevice = 0;
 | 
			
		||||
	int err;
 | 
			
		||||
	
 | 
			
		||||
	err = snd_config_expand(src, NULL, NULL, &e);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	err = snd_config_search(e, "card", &n);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("field card not found");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	err = snd_config_get_integer(n, &card);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("field card is not an integer");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	err = snd_config_search(e, "device", &n);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("field device not found");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	err = snd_config_get_integer(n, &device);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("field device is not an integer");
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	if (snd_config_search(e, "subdevice", &n) >= 0) {
 | 
			
		||||
		err = snd_config_get_integer(n, &subdevice);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			SNDERR("field subdevice is not an integer");
 | 
			
		||||
			goto __error;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	err = open_ctl(card, &ctl);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("could not open control for card %li", card);
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	snd_pcm_info_alloca(&info);
 | 
			
		||||
	snd_pcm_info_set_device(info, device);
 | 
			
		||||
	snd_pcm_info_set_subdevice(info, subdevice);
 | 
			
		||||
	err = snd_ctl_pcm_info(ctl, info);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("snd_ctl_pcm_info error: %s", snd_strerror(err));
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	res = strdup(snd_pcm_info_get_id(info));
 | 
			
		||||
	if (res == NULL) {
 | 
			
		||||
		err = -ENOMEM;
 | 
			
		||||
		goto __error;
 | 
			
		||||
	}
 | 
			
		||||
	*dst = res;
 | 
			
		||||
      __error:
 | 
			
		||||
      	if (ctl)
 | 
			
		||||
      		snd_ctl_close(ctl);
 | 
			
		||||
	snd_config_delete(e);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -478,7 +478,7 @@ int snd_ctl_open_noupdate(snd_ctl_t **ctlp, const char *name, int mode)
 | 
			
		|||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
	if (args) {
 | 
			
		||||
		err = snd_config_expand(ctl_conf, args, &ctl_conf);
 | 
			
		||||
		err = snd_config_expand(ctl_conf, args, NULL, &ctl_conf);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1035,12 +1035,10 @@ static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
 | 
			
		|||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (args == NULL && snd_config_search(pcm_conf, "$", &conf) >= 0) /* expand arguments */
 | 
			
		||||
		args = "";
 | 
			
		||||
	if (args) {
 | 
			
		||||
		err = snd_config_expand(pcm_conf, args, &pcm_conf);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
	err = snd_config_expand(pcm_conf, args, NULL, &pcm_conf);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("Could not expand configuration: %s", snd_strerror(err));
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
	if (snd_config_search(pcm_conf, "redirect", &conf) >= 0) {
 | 
			
		||||
		snd_config_t *tmp_conf;
 | 
			
		||||
| 
						 | 
				
			
			@ -1049,8 +1047,10 @@ static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
 | 
			
		|||
		err = snd_config_redirect_load(root, conf, &redir_name, &tmp_conf, &conf_free_tmp);
 | 
			
		||||
		if (args)
 | 
			
		||||
			snd_config_delete(pcm_conf);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			SNDERR("Redirect error: %s", snd_strerror(err));
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
		err = snd_pcm_open_noupdate(pcmp, tmp_conf, redir_name, stream, mode);
 | 
			
		||||
		if (redir_name)
 | 
			
		||||
			free(redir_name);
 | 
			
		||||
| 
						 | 
				
			
			@ -1059,8 +1059,7 @@ static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
 | 
			
		|||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
	err = snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode);
 | 
			
		||||
	if (args)
 | 
			
		||||
		snd_config_delete(pcm_conf);
 | 
			
		||||
	snd_config_delete(pcm_conf);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -175,7 +175,7 @@ int snd_rawmidi_open_noupdate(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
 | 
			
		|||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
	if (args) {
 | 
			
		||||
		err = snd_config_expand(rawmidi_conf, args, &rawmidi_conf);
 | 
			
		||||
		err = snd_config_expand(rawmidi_conf, args, NULL, &rawmidi_conf);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -161,7 +161,7 @@ static int snd_seq_open_noupdate(snd_seq_t **seqp, const char *name,
 | 
			
		|||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
	if (args) {
 | 
			
		||||
		err = snd_config_expand(seq_conf, args, &seq_conf);
 | 
			
		||||
		err = snd_config_expand(seq_conf, args, NULL, &seq_conf);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue