comparison scripts/dgen.rb @ 11:07194b026fa4

Added bindings to a couple of frameworks, new license + some other things
author Jacob Carlborg <doob@me.com>
date Sat, 01 Aug 2009 15:03:28 +0200
parents 27e00625790b
children 4f583f7e242e
comparison
equal deleted inserted replaced
10:27e00625790b 11:07194b026fa4
180 end 180 end
181 end 181 end
182 182
183 # This Struct represents a C/Objective-C header 183 # This Struct represents a C/Objective-C header
184 HeaderFile = Struct.new(:name, :framework, :cftypes, :constants, :c_constants, :d_constants, :d_constants_static_this, :defines, 184 HeaderFile = Struct.new(:name, :framework, :cftypes, :constants, :c_constants, :d_constants, :d_constants_static_this, :defines,
185 :enums, :functions, :c_functions, :function_pointers, :function_wrappers, :imports, :path, :structs, :typedefs) do 185 :enums, :enums_gnu, :needs_type_encoding, :functions, :c_functions, :function_pointers, :function_wrappers, :imports, :path, :structs, :typedefs) do
186 def initialize 186 def initialize
187 self.name = "" 187 self.name = ""
188 self.cftypes = [] 188 self.cftypes = []
189 self.constants = [] 189 self.constants = []
190 self.defines = [] 190 self.defines = []
191 self.enums = [] 191 self.enums = []
192 self.enums_gnu = []
193 self.needs_type_encoding = false
192 self.framework = "" 194 self.framework = ""
193 self.functions = [] 195 self.functions = []
194 self.c_functions = [] 196 self.c_functions = []
195 self.function_pointers = [] 197 self.function_pointers = []
196 self.function_wrappers = [] 198 self.function_wrappers = []
223 @frameworks = [] 225 @frameworks = []
224 @function_wrappers = [] 226 @function_wrappers = []
225 @c_functions = [] 227 @c_functions = []
226 @headers = [] 228 @headers = []
227 @package = "dstep" 229 @package = "dstep"
230 @needs_bridge = false
228 end 231 end
229 232
230 # Generates the D code from the xml metadata 233 # Generates the D code from the xml metadata
231 def generate_code 234 def generate_code
232 @files.each do |dstep_file| 235 @files.each do |dstep_file|
233 xml = XmlSimple.xml_in(dstep_file) 236 xml = XmlSimple.xml_in(dstep_file)
237
238 @needs_bridge = !xml.protocol.nil? || !xml.category.nil? || !xml["class"].nil?
239
240 interfaces(xml.protocol) unless xml.protocol.nil?
241 templates(xml.category) unless xml.category.nil?
242 classes(xml["class"]) unless xml["class"].nil?
234 243
235 unless xml.framework.nil? 244 unless xml.framework.nil?
236 frameworks = xml.framework 245 frameworks = xml.framework
237 246
238 frameworks.each do |frame| 247 frameworks.each do |frame|
244 header.name = file.name 253 header.name = file.name
245 header.constants = constants(file.constant) unless file.constant.nil? 254 header.constants = constants(file.constant) unless file.constant.nil?
246 header.c_constants = c_constants unless file.constant.nil? 255 header.c_constants = c_constants unless file.constant.nil?
247 header.d_constants = d_constants unless file.constant.nil? 256 header.d_constants = d_constants unless file.constant.nil?
248 header.d_constants_static_this = d_constants_static_this unless file.constant.nil? 257 header.d_constants_static_this = d_constants_static_this unless file.constant.nil?
249 header.enums = enums(file.enum) unless file.enum.nil? 258 header.enums, header.enums_gnu, header.needs_type_encoding = enums(file.enum, header.name) unless file.enum.nil?
250 header.functions = functions(file.function) unless file.function.nil? 259 header.functions = functions(file.function) unless file.function.nil?
251 header.c_functions = c_functions unless file.function.nil? 260 header.c_functions = c_functions unless file.function.nil?
252 header.function_pointers = function_pointers(file.functionPointer) unless file.functionPointer.nil? 261 header.function_pointers = function_pointers(file.functionPointer) unless file.functionPointer.nil?
253 header.function_wrappers = function_wrappers unless file.function.nil? 262 header.function_wrappers = function_wrappers unless file.function.nil?
254 header.imports = imports(file.import, file.name, framework.name) unless file.import.nil? 263 header.imports = imports(header, file.import, file.name, framework.name) unless file.import.nil?
255 header.structs = structs(file.struct) unless file.struct.nil? 264 header.structs = structs(file.struct) unless file.struct.nil?
256 265
257 header.typedefs = typedefs(file.typedef) unless file.typedef.nil? 266 header.typedefs = typedefs(file.typedef) unless file.typedef.nil?
258 267
259 framework.files << header 268 framework.files << header
271 header.name = file.name 280 header.name = file.name
272 header.constants = constants(file.constant) unless file.constant.nil? 281 header.constants = constants(file.constant) unless file.constant.nil?
273 header.c_constants = c_constants unless file.constant.nil? 282 header.c_constants = c_constants unless file.constant.nil?
274 header.d_constants = d_constants unless file.constant.nil? 283 header.d_constants = d_constants unless file.constant.nil?
275 header.d_constants_static_this = d_constants_static_this unless file.constant.nil? 284 header.d_constants_static_this = d_constants_static_this unless file.constant.nil?
276 header.enums = enums(file.enum) unless file.enum.nil? 285 header.enums, header.enums_gnu, header.needs_type_encoding = enums(file.enum) unless file.enum.nil?
277 header.functions = functions(file.function) unless file.function.nil? 286 header.functions = functions(file.function) unless file.function.nil?
278 header.function_pointers = function_pointers(file.functionPointer) unless file.functionPointer.nil? 287 header.function_pointers = function_pointers(file.functionPointer) unless file.functionPointer.nil?
279 header.c_functions = c_functions unless file.function.nil? 288 header.c_functions = c_functions unless file.function.nil?
280 header.function_wrappers = function_wrappers unless file.function.nil? 289 header.function_wrappers = function_wrappers unless file.function.nil?
281 header.imports = imports(file.import, file.name) unless file.import.nil? 290 header.imports = imports(header, file.import, file.name) unless file.import.nil?
282 header.structs = structs(file.struct) unless file.struct.nil? 291 header.structs = structs(file.struct) unless file.struct.nil?
283 header.typedefs = typedefs(file.typedef) unless file.typedef.nil? 292 header.typedefs = typedefs(file.typedef) unless file.typedef.nil?
284 293
285 @headers << header 294 @headers << header
286 end 295 end
287 end 296 end
288
289 interfaces(xml.protocol) unless xml.protocol.nil?
290 templates(xml.category) unless xml.category.nil?
291 classes(xml["class"]) unless xml["class"].nil?
292 end 297 end
293 end 298 end
294 299
295 # Outputs the generate D code 300 # Outputs the generate D code
296 def output_code 301 def output_code
297 @frameworks.each do |framework| 302 @frameworks.each do |framework|
298 framework_path = framework_path = "#{@out_dir}/#{@package}/#{get_identifier(get_framework_name(framework.name.downcase))}" 303 framework_path = framework_path = "#{@out_dir}/#{@package}/#{get_identifier(get_framework_name(framework.name.downcase))}" unless sub_framework?(framework.name.downcase)
304 framework_path = framework_path = "#{@out_dir}/#{@package}/#{get_identifier(get_parent_framework_name(framework.name.downcase))}/#{get_identifier(get_framework_name(framework.name.downcase))}" if sub_framework?(framework.name.downcase)
299 305
300 FileUtils.mkdir_p(framework_path) unless File.exist?(framework_path) 306 FileUtils.mkdir_p(framework_path) unless File.exist?(framework_path)
301 307
302 framework.files.each do |header| 308 framework.files.each do |header|
303 file_path = "#{framework_path}/#{get_identifier(header.name)}" 309 file_path = "#{framework_path}/#{get_identifier(header.name)}"
304 bindings_file_path = file_path + "_bindings.d" 310 bindings_file_path = file_path + "_bindings.d"
305 file_path << ".d" 311 file_path << ".d"
306 mod = "#{@package}.#{get_identifier(get_framework_name(framework.name.downcase))}.#{get_identifier(header.name)}" 312 mod = "#{@package}.#{get_identifier(get_framework_name(framework.name.downcase))}.#{get_identifier(header.name)}" unless sub_framework?(framework.name.downcase)
313 mod = "#{@package}.#{get_identifier(get_parent_framework_name(framework.name.downcase))}.#{get_identifier(get_framework_name(framework.name.downcase))}.#{get_identifier(header.name)}" if sub_framework?(framework.name.downcase)
307 314
308 File.open(file_path, "w") do |file| 315 File.open(file_path, "w") do |file|
309 file << copyright 316 file << copyright
310 file << "module #{mod};" 317 file << "module #{mod};"
311 file << header.imports 318 file << header.imports
312 file << "\n" if header.d_constants.nil? || header.function_wrappers.nil?
313 file << "import bindings = #{mod}_bindings".nl.nl(false) unless header.d_constants.nil? || header.function_wrappers.nil? 319 file << "import bindings = #{mod}_bindings".nl.nl(false) unless header.d_constants.nil? || header.function_wrappers.nil?
314 file << header.defines 320 file << header.defines
315 file << header.typedefs 321 file << header.typedefs
316 file << header.cftypes 322 file << header.cftypes
317 file << header.function_pointers 323 file << header.function_pointers
318 file << header.c_constants 324 file << header.c_constants
319 file << header.d_constants 325 file << header.d_constants
326 file << header.enums_gnu if header.needs_type_encoding
320 file << header.enums 327 file << header.enums
321 file << header.structs 328 file << header.structs
322 329
323 unless header.d_constants_static_this.length == 0 330 unless header.d_constants_static_this.length == 0
324 file << "static this ()".nl(false) 331 file << "static this ()".nl(false)
374 381
375 File.open(header_path, "w") do |file| 382 File.open(header_path, "w") do |file|
376 file << copyright 383 file << copyright
377 file << "module #{mod};" 384 file << "module #{mod};"
378 file << header.imports 385 file << header.imports
379 file << "\n" if header.d_constants.nil? || header.function_wrappers.nil?
380 file << "import bindings = #{mod}_bindings".nl.nl(false) unless header.d_constants.nil? || header.function_wrappers.nil? 386 file << "import bindings = #{mod}_bindings".nl.nl(false) unless header.d_constants.nil? || header.function_wrappers.nil?
381 file << header.defines 387 file << header.defines
382 file << header.typedefs 388 file << header.typedefs
383 file << header.cftypes 389 file << header.cftypes
384 file << header.function_pointers 390 file << header.function_pointers
385 file << header.c_constants 391 file << header.c_constants
386 file << header.d_constants 392 file << header.d_constants
393 file << header.enums_gnu if header.needs_type_encoding
387 file << header.enums 394 file << header.enums
388 file << header.structs 395 file << header.structs
389 396
390 unless header.d_constants_static_this.nil? 397 unless header.d_constants_static_this.nil?
391 file << "static this ()".nl(false) 398 file << "static this ()".nl(false)
752 @d_constants_static_this.clear 759 @d_constants_static_this.clear
753 str.string 760 str.string
754 end 761 end
755 762
756 # Generates the D code for the enums 763 # Generates the D code for the enums
757 def enums (enums) 764 def enums (enums, header_name)
758 return "" if enums.length == 0 765 return "" if enums.length == 0
759 766
760 str = StringIO.new 767 str = StringIO.new
768 str_gnu = StringIO.new
769 consts = []
761 770
762 enums.each do |enum| 771 enums.each do |enum|
772 localConsts = []
773 str_gnu2 = StringIO.new
763 str << "enum" 774 str << "enum"
764 775 needs_type_encoding = false
776
765 if enum.name.length == 0 777 if enum.name.length == 0
766 str << "\n{".nl(false) 778 str << "\n{".nl(false)
767 779
768 enum.member.each_with_index do |member, i| 780 needs_type_encoding, localConsts = enum_helper(enum, str, str_gnu2, header_name)
769 str << get_identifier(member["name"]).indent
770 str << " = " if member.value.length > 0
771 str << member.value
772 str << ",".nl(false) unless i == enum.member.length - 1
773 end
774 else 781 else
775 str << " " 782 str << " "
776 str << get_identifier(enum["name"]).nl(false) 783 str << get_identifier(enum["name"]).nl(false)
777 str << "{".nl(false) 784 str << "{".nl(false)
778 785
779 enum["member"].each_with_index do |member, i| 786 needs_type_encoding, localConsts = enum_helper(enum, str, str_gnu2, header_name)
780 str << get_identifier(member["name"]).indent 787 end
781 str << " = " if member.value.length > 0 788
789 str << "\n}".nl(false).nl(false)
790
791 # if needs_type_encoding
792 # str_gnu << "// This is needed otherwise the enums will fail compiling with gdc\n"
793 # str_gnu << "version (GNU)\n{\n"
794 # str_gnu << "private\n".indent
795 # str_gnu << "{\n".indent
796 # str_gnu << str_gnu2.string
797 # str_gnu << "\n"
798 # str_gnu << "}".indent
799 # str_gnu << "\n}".nl(false).nl(false)
800 # end
801
802 consts << localConsts
803 end
804
805 consts.flatten!
806
807 if @needs_type_encoding && consts.length > 0
808 str_gnu << "// This is needed otherwise the enums will fail compiling with gdc\n"
809 str_gnu << "version (GNU)\n{\n"
810 str_gnu << "private\n".indent
811 str_gnu << "{\n".indent
812
813 consts.each do |const|
814 str_gnu << "const __".indent(2)
815 str_gnu << const.name
816 str_gnu << ' = getOSType!("'
817 str_gnu << const.value
818 str_gnu << '")'.nl
819 end
820
821 str_gnu << "}".indent
822 str_gnu << "\n}".nl(false).nl(false)
823 end
824
825 needs_type_encoding = @needs_type_encoding
826 @needs_type_encoding = false
827
828 return str.string, str_gnu.string, needs_type_encoding
829 end
830
831 def enum_helper (enum, str, str_gnu, header_name)
832 needs_type_encoding = false
833 consts = []
834
835 enum.member.each_with_index do |member, i|
836 str << get_identifier(member.name).indent
837
838 if member.value.length > 0
839 needs_type_encoding = true if member.value[0, 1] == "'"
840 @needs_type_encoding = true if needs_type_encoding
841 str << " = "
842
843 if member.value[0, 1] == "'"
844 str << 'getOSType!("'
845 str << member.value[1 ... -1]
846 str << '")'
847
848 consts << { :name => get_identifier(member.name), :value => member.value[1 ... -1] }
849 # str_gnu << "const __".indent(2)
850 # str_gnu << get_identifier(member.name)
851 # str_gnu << " = "
852 # str_gnu << 'getOSType!("'
853 # str_gnu << member.value[1 ... -1]
854 # str_gnu << '")'.nl
855 else
782 str << member.value 856 str << member.value
783 str << ",".nl(false) unless i == enum.member.length - 1
784 end 857 end
785 end 858 end
786 859
787 str << "\n}".nl(false).nl(false) 860 str << ",".nl(false) unless i == enum.member.length - 1
788 end 861 end
789 862
790 str.string 863 return needs_type_encoding, consts
791 end 864 end
792 865
793 # Generates the D code for the function/method args 866 # Generates the D code for the function/method args
794 def args (args, variadic, method = false) 867 def args (args, variadic, method = false)
795 return "" if args.nil? 868 return "" if args.nil?
796 869
797 str = StringIO.new 870 str = StringIO.new
973 @function_wrappers.clear 1046 @function_wrappers.clear
974 str.string 1047 str.string
975 end 1048 end
976 1049
977 # Generates the D code for the imports 1050 # Generates the D code for the imports
978 def imports (imports, filename, framework_name = nil) 1051 def imports (header, imports, filename, framework_name = nil)
979 return "" if imports.length == 0 1052 return "" if imports.length == 0
980 1053
981 str = StringIO.new 1054 str = StringIO.new
982 1055
983 imports.each do |import| 1056 imports.each do |import|
994 1067
995 str << import 1068 str << import
996 str << splits[-1 .. -1] 1069 str << splits[-1 .. -1]
997 end 1070 end
998 1071
999 str << "import dstep.objc.bridge.Bridge".nl 1072 str << "import dstep.objc.bridge.TypeEncoding".nl if header.needs_type_encoding && !@needs_bridge
1000 str << "import dstep.objc.objc : id".nl 1073
1074 if @needs_bridge
1075 str << "import dstep.objc.bridge.Bridge".nl
1076 str << "import dstep.objc.bridge.TypeEncoding".nl if header.needs_type_encoding
1077 str << "import dstep.objc.objc : id".nl
1078 end
1001 1079
1002 str << "\n\n" 1080 str << "\n\n"
1003 str = str.string.sort.to_s 1081 str = str.string.sort.to_s
1004 1082 str << "\n"
1005 return "\n\npublic:" << str if filename == framework_name 1083
1084 return "\n\npublic:" << str if filename == get_framework_name(framework_name)
1006 return str 1085 return str
1007 end 1086 end
1008 1087
1009 # Generates the D code for the methods 1088 # Generates the D code for the methods
1010 def methods (methods, class_name) 1089 def methods (methods, class_name)
1119 struct = true 1198 struct = true
1120 else 1199 else
1121 type = get_type(typedef.type, typedef.type64, typedef.declaredType) 1200 type = get_type(typedef.type, typedef.type64, typedef.declaredType)
1122 end 1201 end
1123 1202
1124 if struct 1203 if struct
1125 str << typedef.declaredType.gsub("*", "").nl 1204 unless type.name == typedef.name
1126 str << "alias " 1205 declaredType = typedef.declaredType
1127 str << get_identifier(type.name) 1206 declaredType = declaredType[7 .. -1] if declaredType.length > 7 && declaredType[0 ... 6] == "struct"
1128 str << "*" if typedef.declaredType =~ /\*/ 1207
1129 str << " " 1208 unless declaredType == typedef.name
1130 str << get_identifier(typedef.name).nl 1209 str << "alias "
1210 str << get_identifier(declaredType)
1211 str << " "
1212 str << get_identifier(typedef.name).nl
1213 end
1214
1215 # str << "alias "
1216 # str << get_identifier(type.name)
1217 # str << "*" if typedef.declaredType =~ /\*{1}/
1218 # str << " "
1219 # str << get_identifier(typedef.name).nl
1220 end
1131 else 1221 else
1132 str << "alias " 1222 str << "alias "
1133 str << type 1223 str << type
1134 str << " " 1224 str << " "
1135 str << get_identifier(typedef.name).nl 1225 str << get_identifier(typedef.name).nl
1183 # 1273 #
1184 def get_method_name (selector) 1274 def get_method_name (selector)
1185 i = selector.index_of(":") 1275 i = selector.index_of(":")
1186 get_identifier(selector[0 ... i]) 1276 get_identifier(selector[0 ... i])
1187 end 1277 end
1278
1279 def get_matching_close_char (char)
1280 case char
1281 when "{"; return "}"
1282 when "("; return ")"
1283 when "["; return "]"
1284 else
1285 char
1286 end
1287 end
1288
1289
1188 1290
1189 # Gets the D type from the encoded C/Objective-C type 1291 # Gets the D type from the encoded C/Objective-C type
1190 # type:: the type 1292 # type:: the type
1191 # type64:: the type for 64bit targets 1293 # type64:: the type for 64bit targets
1192 # declared_type:: the declared type 1294 # declared_type:: the declared type
1241 str = "" 1343 str = ""
1242 t = type[1 ... -1] 1344 t = type[1 ... -1]
1243 count = $1 if t =~ /(\d+)/ 1345 count = $1 if t =~ /(\d+)/
1244 t = $' 1346 t = $'
1245 1347
1246 t64 = type64[1 ... -1] 1348 t64 = ""
1247 count = $1 if t64 =~ /(\d+)/ 1349
1248 t64 = $' 1350 unless type64.nil?
1351 t64 = type64[1 ... -1]
1352 count = $1 if t64 =~ /(\d+)/
1353 t64 = $'
1354 else
1355 t64 = t
1356 end
1249 1357
1250 str = get_type(t, t64, declared_type) 1358 str = get_type(t, t64, declared_type)
1251 str << "[#{count}]" 1359 str << "[#{count}]"
1252 1360
1253 return get_identifier(str) 1361 return get_identifier(str)
1254 when "(" 1362 when "("
1255 resolved_types = [] 1363 resolved_types = []
1256 types = $2 if type =~ /\((.+)=(.+)\)/ 1364 types = $2 if type =~ /\((.+)=(.+)\)/
1257 types64 = $2 if type64 =~ /\((.+)=(.+)\)/ 1365 types64 = $2 if type64 =~ /\((.+)=(.+)\)/
1258 1366 i = 0
1259 for i in types 1367
1260 resolved_types << get_type(types[i], types64[i], declared_type) 1368 while i < types.length
1261 end 1369 t = types[i, 1]
1262 1370 t64 = types64.nil? ? t : types64[i, 1]
1263 return get_identifier(resolved_types) 1371
1264 when "^" 1372 index = t =~ /(\(|\[|\{)/
1265 if type == "^@" 1373
1266 return get_identifier(get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup + "*") 1374 unless index.nil?
1267 elsif type.length > 2 && type[0 ... 2] == "^^" 1375 x = types.index(get_matching_close_char($1), index)
1268 return get_identifier(get_type(type[2 .. -1], type64[2 .. -1], declared_type).dup + "**") 1376 t = types[index .. x]
1269 elsif type == "^?" # assuming function pointer 1377 t64 = types64.nil? ? t : types64[index .. x]
1270 tmp = cfp_to_dfp(type) 1378 i += x - index
1271 return get_identifier(tmp) unless tmp.nil? 1379 end
1272 end 1380
1273 1381 resolved_types << get_type(t, t64, declared_type)
1274 if !type.nil? && !type64.nil? 1382 i += 1
1275 t = get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup 1383 end unless types.nil?
1276 1384
1277 return get_identifier(t) if t =~ /\*/ 1385 get_identifier(resolved_types)
1278 return get_identifier(t + "*") if t !~ /\*/ 1386 when "^"
1279 elsif !type.nil? 1387 t = type[1 .. -1]
1280 t = get_type(type[1 .. -1], type64, declared_type).dup << "*" 1388 t64 = type64.nil? ? t : type64[1 .. -1]
1281 1389 get_identifier(get_type(t, t64, declared_type).dup + "*")
1282 return get_identifier(t) if t =~ /\*/ 1390
1283 return get_identifier(t + "*") if t !~ /\*/ 1391 # if type == "^@"
1284 end 1392 # return get_identifier(get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup + "*")
1393 # elsif type.length > 2 && type[0 ... 2] == "^^"
1394 # return get_identifier(get_type(type[2 .. -1], type64[2 .. -1], declared_type).dup + "**")
1395 # elsif type == "^?" # assuming function pointer
1396 # tmp = cfp_to_dfp(type)
1397 # return get_identifier(tmp) unless tmp.nil?
1398 # end
1399 #
1400 # if !type.nil? && !type64.nil?
1401 # t = get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup
1402 #
1403 # return get_identifier(t) if t =~ /\*/
1404 # return get_identifier(t + "*") if t !~ /\*/
1405 # elsif !type.nil?
1406 # t = get_type(type[1 .. -1], type64, declared_type).dup << "*"
1407 #
1408 # return get_identifier(t) if t =~ /\*/
1409 # return get_identifier(t + "*") if t !~ /\*/
1410 # end
1285 when "{" 1411 when "{"
1286 return get_identifier(declared_type) 1412 return get_identifier(declared_type)
1287 end 1413 end
1288 end 1414 end
1289 1415
1519 i = framework.rindex(".framework") 1645 i = framework.rindex(".framework")
1520 return framework if i.nil? 1646 return framework if i.nil?
1521 x = framework.rindex("/", i) 1647 x = framework.rindex("/", i)
1522 framework[x + 1 ... i] 1648 framework[x + 1 ... i]
1523 end 1649 end
1650
1651 def sub_framework? (framework)
1652 i = framework.index("framework")
1653 return false if i.nil?
1654 !framework.index("framework", i + 1).nil?
1655 end
1656
1657 def get_parent_framework_name (framework)
1658 return get_framework_name(framework) unless sub_framework?(framework)
1659 i = framework.index(".framework")
1660 return framework if i.nil?
1661 str = framework[0 ... i]
1662 x = str.rindex("/")
1663 return str if x.nil?
1664 str[x + 1 .. -1]
1665 end
1524 end 1666 end
1525 1667
1526 # Prints the message to stderr, exits 1668 # Prints the message to stderr, exits
1527 def die (*msg) 1669 def die (*msg)
1528 $stderr.puts msg 1670 $stderr.puts msg