@@ -21,6 +21,12 @@ import (
2121	"strings" 
2222)
2323
24+ // ValidateQualifiedName checks the validity of a fully-qualified device name. 
25+ func  ValidateQualifiedName (name  string ) error  {
26+ 	_ , _ , _ , err  :=  ParseFullyQualifiedName (name )
27+ 	return  err 
28+ }
29+ 
2430// ValidateKind checks the validity of a CDI kind. 
2531// The syntax for a device kind“ is 
2632// 
@@ -74,19 +80,19 @@ func validateVendorOrClassName(name string) error {
7480	if  name  ==  ""  {
7581		return  fmt .Errorf ("empty name" )
7682	}
77- 	if  ! IsLetter (rune (name [0 ])) {
83+ 	if  ! isLetter (rune (name [0 ])) {
7884		return  fmt .Errorf ("%q, should start with letter" , name )
7985	}
8086	for  _ , c  :=  range  string (name [1  : len (name )- 1 ]) {
8187		switch  {
82- 		case  IsAlphaNumeric (c ):
88+ 		case  isAlphaNumeric (c ):
8389		case  c  ==  '_'  ||  c  ==  '-'  ||  c  ==  '.' :
8490		default :
8591			return  fmt .Errorf ("invalid character '%c' in name %q" ,
8692				c , name )
8793		}
8894	}
89- 	if  ! IsAlphaNumeric (rune (name [len (name )- 1 ])) {
95+ 	if  ! isAlphaNumeric (rune (name [len (name )- 1 ])) {
9096		return  fmt .Errorf ("%q, should end with a letter or digit" , name )
9197	}
9298
@@ -102,38 +108,107 @@ func ValidateDeviceName(name string) error {
102108	if  name  ==  ""  {
103109		return  fmt .Errorf ("invalid (empty) device name" )
104110	}
105- 	if  ! IsAlphaNumeric (rune (name [0 ])) {
111+ 	if  ! isAlphaNumeric (rune (name [0 ])) {
106112		return  fmt .Errorf ("invalid class %q, should start with a letter or digit" , name )
107113	}
108114	if  len (name ) ==  1  {
109115		return  nil 
110116	}
111117	for  _ , c  :=  range  string (name [1  : len (name )- 1 ]) {
112118		switch  {
113- 		case  IsAlphaNumeric (c ):
119+ 		case  isAlphaNumeric (c ):
114120		case  c  ==  '_'  ||  c  ==  '-'  ||  c  ==  '.'  ||  c  ==  ':' :
115121		default :
116122			return  fmt .Errorf ("invalid character '%c' in device name %q" ,
117123				c , name )
118124		}
119125	}
120- 	if  ! IsAlphaNumeric (rune (name [len (name )- 1 ])) {
126+ 	if  ! isAlphaNumeric (rune (name [len (name )- 1 ])) {
121127		return  fmt .Errorf ("invalid name %q, should end with a letter or digit" , name )
122128	}
123129	return  nil 
124130}
125131
126- // IsLetter reports whether the rune is a letter. 
127- func  IsLetter (c  rune ) bool  {
132+ // ParseFullyQualifiedName splits a fully-qualified name into device vendor, class, 
133+ // and name. If the device fails to parse as a qualified name, or if any 
134+ // of the split components fail to pass syntax validation, vendor and 
135+ // class are returned as empty, together with the verbatim input as the 
136+ // name and an error describing the reason for failure. 
137+ func  ParseFullyQualifiedName (device  string ) (string , string , string , error ) {
138+ 	vendor , class , name  :=  parseDevice (device )
139+ 
140+ 	if  vendor  ==  ""  {
141+ 		return  "" , "" , device , fmt .Errorf ("unqualified device %q, missing vendor" , device )
142+ 	}
143+ 	if  class  ==  ""  {
144+ 		return  "" , "" , device , fmt .Errorf ("unqualified device %q, missing class" , device )
145+ 	}
146+ 	if  name  ==  ""  {
147+ 		return  "" , "" , device , fmt .Errorf ("unqualified device %q, missing device name" , device )
148+ 	}
149+ 
150+ 	if  err  :=  ValidateVendorName (vendor ); err  !=  nil  {
151+ 		return  "" , "" , device , fmt .Errorf ("invalid device %q: %w" , device , err )
152+ 	}
153+ 	if  err  :=  ValidateClassName (class ); err  !=  nil  {
154+ 		return  "" , "" , device , fmt .Errorf ("invalid device %q: %w" , device , err )
155+ 	}
156+ 	if  err  :=  ValidateDeviceName (name ); err  !=  nil  {
157+ 		return  "" , "" , device , fmt .Errorf ("invalid device %q: %w" , device , err )
158+ 	}
159+ 
160+ 	return  vendor , class , name , nil 
161+ }
162+ 
163+ // parseDevice tries to split a device name into vendor, class, and name. 
164+ // If this fails, for instance in the case of unqualified device names, 
165+ // parseDevice returns an empty vendor and class together with name set 
166+ // to the verbatim input. 
167+ func  parseDevice (device  string ) (string , string , string ) {
168+ 	if  device  ==  ""  ||  device [0 ] ==  '/'  {
169+ 		return  "" , "" , device 
170+ 	}
171+ 
172+ 	parts  :=  strings .SplitN (device , "=" , 2 )
173+ 	if  len (parts ) !=  2  ||  parts [0 ] ==  ""  ||  parts [1 ] ==  ""  {
174+ 		return  "" , "" , device 
175+ 	}
176+ 
177+ 	name  :=  parts [1 ]
178+ 	vendor , class  :=  ParseKind (parts [0 ])
179+ 	if  vendor  ==  ""  {
180+ 		return  "" , "" , device 
181+ 	}
182+ 
183+ 	return  vendor , class , name 
184+ }
185+ 
186+ // ParseKind splits a device qualifier into vendor and class. 
187+ // The syntax for a device qualifier is 
188+ // 
189+ //	"<vendor>/<class>" 
190+ // 
191+ // If parsing fails, an empty vendor and the class set to the 
192+ // verbatim input is returned. 
193+ func  ParseKind (kind  string ) (string , string ) {
194+ 	parts  :=  strings .SplitN (kind , "/" , 2 )
195+ 	if  len (parts ) !=  2  ||  parts [0 ] ==  ""  ||  parts [1 ] ==  ""  {
196+ 		return  "" , kind 
197+ 	}
198+ 	return  parts [0 ], parts [1 ]
199+ }
200+ 
201+ // isLetter reports whether the rune is a letter. 
202+ func  isLetter (c  rune ) bool  {
128203	return  ('A'  <=  c  &&  c  <=  'Z' ) ||  ('a'  <=  c  &&  c  <=  'z' )
129204}
130205
131206// IsDigit reports whether the rune is a digit. 
132- func  IsDigit (c  rune ) bool  {
207+ func  isDigit (c  rune ) bool  {
133208	return  '0'  <=  c  &&  c  <=  '9' 
134209}
135210
136- // IsAlphaNumeric  reports whether the rune is a letter or digit. 
137- func  IsAlphaNumeric (c  rune ) bool  {
138- 	return  IsLetter (c ) ||  IsDigit (c )
211+ // isAlphaNumeric  reports whether the rune is a letter or digit. 
212+ func  isAlphaNumeric (c  rune ) bool  {
213+ 	return  isLetter (c ) ||  isDigit (c )
139214}
0 commit comments