From patchwork Thu Sep 20 17:01:44 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Lance Taylor X-Patchwork-Id: 185434 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 9DA572C008D for ; Fri, 21 Sep 2012 03:02:23 +1000 (EST) Comment: DKIM? See http://www.dkim.org DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gcc.gnu.org; s=default; x=1348765344; h=Comment: DomainKey-Signature:Received:Received:Received:Received:Received: Received:Received:From:To:Subject:Date:Message-ID:User-Agent: MIME-Version:Content-Type:Mailing-List:Precedence:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:Sender: Delivered-To; bh=judsa9lgeKKFbyQhpnsjiSGXN80=; b=YI6GTEkncf3Wxi3 6257mEsz5oZzmqGuprY3cF00pJnV2jlHh2jDqg8FSCarlY6w0QvfpdKRVr6Yku3r aC4WOkbYO+wbOF14/AsQnSLMLlqgy4iszt5e+l3KmwVyvfib88zuyj68lEJhlrib PdyE9n9HuUz1rGr6lRGwZuVLAK9M= Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=gcc.gnu.org; h=Received:Received:X-SWARE-Spam-Status:X-Spam-Check-By:Received:Received:X-Google-DKIM-Signature:Received:Received:Received:From:To:Subject:Date:Message-ID:User-Agent:MIME-Version:Content-Type:X-Gm-Message-State:X-IsSubscribed:Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:Sender:Delivered-To; b=gsjm7nfqPhN6h7DApuKgmfWql1C2w7iq8yqp01UW/Lof56oqu7Uz/PmDO5ymqk 1HJiUxaW1oo8wSbov6bIUpqO9kd4rMC+1PaOsXSProvfh264SISTx6MfMoFU2J7d p+AL6wpuob6yZ7p6G2D5Qh2MtL91Okl615GI2aOg1gFRs=; Received: (qmail 9311 invoked by alias); 20 Sep 2012 17:02:13 -0000 Received: (qmail 9277 invoked by uid 22791); 20 Sep 2012 17:02:06 -0000 X-SWARE-Spam-Status: No, hits=-4.9 required=5.0 tests=AWL, BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FILL_THIS_FORM_SHORT, KHOP_RCVD_TRUST, RCVD_IN_DNSWL_LOW, RCVD_IN_HOSTKARMA_YE, RP_MATCHES_RCVD, T_TVD_MIME_NO_HEADERS X-Spam-Check-By: sourceware.org Received: from mail-pb0-f47.google.com (HELO mail-pb0-f47.google.com) (209.85.160.47) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 20 Sep 2012 17:01:48 +0000 Received: by pbcwy7 with SMTP id wy7so5909409pbc.20 for ; Thu, 20 Sep 2012 10:01:47 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:subject:date:message-id:user-agent:mime-version :content-type:x-gm-message-state; bh=oVg+16dh55YfvjTi7KPUo6s85QMl0pfdetuiWrsbuuM=; b=c0emV2nvdJjudVDp9fS5lX07oZo13IAbaUbAkNsTCQedhFLLeelNZ8CH9KBGtx6/Hy s6EEl/2jVGTxfQtB7tyahxtiFHj08Jeiw/zQkpSY7KFRsN0Mtuh6lW6UyUxNeVryZP9i TeFNXjpj4wwAXYPAmgO5LcZDS+v9JZUieP4McPtHcZ9QSrcrokcWvibLUAQIwcvWB8RY jj4PKfE08NMBgklwxEpxJfK9hZO8yS3TWgoPDhAlqPk8kl/BY5xq4P+XcNEQkV4hjWJU 1VXEuggHy/Ppfy8zNuTrGKmOWQrbG9HpWC3LxQeAMDCzaUMNhoLlvZxqJw1tKWJNPG95 8Jbw== Received: by 10.68.221.70 with SMTP id qc6mr8544374pbc.92.1348160507737; Thu, 20 Sep 2012 10:01:47 -0700 (PDT) Received: by 10.68.221.70 with SMTP id qc6mr8544344pbc.92.1348160507487; Thu, 20 Sep 2012 10:01:47 -0700 (PDT) Received: from coign.google.com ([172.19.252.231]) by mx.google.com with ESMTPS id g1sm2709221paz.18.2012.09.20.10.01.45 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 20 Sep 2012 10:01:46 -0700 (PDT) From: Ian Lance Taylor To: gcc-patches@gcc.gnu.org Subject: Go 4.7 patch committed: Bring 4.7 branch up to date Date: Thu, 20 Sep 2012 10:01:44 -0700 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (gnu/linux) MIME-Version: 1.0 X-Gm-Message-State: ALoCoQl7T968wszGXc9P+mZMxp0WJMwn74NUXpMifWuzUutW/gEmAETz9qALRQuVfxGiMBpul1T3dr72dp0oQkY0vb0FUcJnbcdQVlt+lEBkQHylxAMlSp1G3MFYkSfY4g4K+OnrSdUIMtei7eU9tIULoQfarqeW5Bn5uaxgUHg55YuSP3tkqumBuP9uw7wg/pxx1hMciL8hh4H/zxEjmxjjHpcO1vX54g== X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org I have committed this patch to the GCC 4.7 branch to bring it up to date with the bug fixes that I have already committed to mainline. Bootstrapped 4.7 branch and ran Go testsuite on x86_64-unknown-linux-gnu. Ian 2012-09-20 Ian Lance Taylor * Make-lang.in (go/gogo.o): Depend on filenames.h. Index: gcc/go/Make-lang.in =================================================================== --- gcc/go/Make-lang.in (revision 190405) +++ gcc/go/Make-lang.in (working copy) @@ -289,10 +289,11 @@ go/gogo-tree.o: go/gofrontend/gogo-tree. convert.h output.h $(DIAGNOSTIC_H) $(GO_TYPES_H) \ $(GO_EXPRESSIONS_H) $(GO_STATEMENTS_H) $(GO_RUNTIME_H) \ go/gofrontend/backend.h $(GO_GOGO_H) -go/gogo.o: go/gofrontend/gogo.cc $(GO_SYSTEM_H) $(GO_C_H) \ - go/gofrontend/go-dump.h $(GO_LEX_H) $(GO_TYPES_H) $(GO_STATEMENTS_H) \ - $(GO_EXPRESSIONS_H) go/gofrontend/dataflow.h $(GO_RUNTIME_H) \ - $(GO_IMPORT_H) $(GO_EXPORT_H) go/gofrontend/backend.h $(GO_GOGO_H) +go/gogo.o: go/gofrontend/gogo.cc $(GO_SYSTEM_H) \ + $(srcdir)/../include/filenames.h $(GO_C_H) go/gofrontend/go-dump.h \ + $(GO_LEX_H) $(GO_TYPES_H) $(GO_STATEMENTS_H) $(GO_EXPRESSIONS_H) \ + go/gofrontend/dataflow.h $(GO_RUNTIME_H) $(GO_IMPORT_H) \ + $(GO_EXPORT_H) go/gofrontend/backend.h $(GO_GOGO_H) go/import.o: go/gofrontend/import.cc $(GO_SYSTEM_H) \ $(srcdir)/../include/filenames.h $(srcdir)/../include/simple-object.h \ $(GO_C_H) $(GO_GOGO_H) $(GO_LEX_H) $(GO_TYPES_H) $(GO_EXPORT_H) \ Index: gcc/go/gofrontend/gogo.cc =================================================================== --- gcc/go/gofrontend/gogo.cc (revision 190405) +++ gcc/go/gofrontend/gogo.cc (working copy) @@ -6,6 +6,8 @@ #include "go-system.h" +#include "filenames.h" + #include "go-c.h" #include "go-dump.h" #include "lex.h" @@ -385,6 +387,57 @@ Gogo::import_package(const std::string& bool is_local_name_exported, Location location) { + if (filename.empty()) + { + error_at(location, "import path is empty"); + return; + } + + const char *pf = filename.data(); + const char *pend = pf + filename.length(); + while (pf < pend) + { + unsigned int c; + int adv = Lex::fetch_char(pf, &c); + if (adv == 0) + { + error_at(location, "import path contains invalid UTF-8 sequence"); + return; + } + if (c == '\0') + { + error_at(location, "import path contains NUL"); + return; + } + if (c < 0x20 || c == 0x7f) + { + error_at(location, "import path contains control character"); + return; + } + if (c == '\\') + { + error_at(location, "import path contains backslash; use slash"); + return; + } + if (Lex::is_unicode_space(c)) + { + error_at(location, "import path contains space character"); + return; + } + if (c < 0x7f && strchr("!\"#$%&'()*,:;<=>?[]^`{|}", c) != NULL) + { + error_at(location, "import path contains invalid character '%c'", c); + return; + } + pf += adv; + } + + if (IS_ABSOLUTE_PATH(filename.c_str())) + { + error_at(location, "import path cannot be absolute path"); + return; + } + if (filename == "unsafe") { this->import_unsafe(local_name, is_local_name_exported, location); @@ -1003,7 +1056,15 @@ Gogo::add_type(const std::string& name, Named_object* no = this->current_bindings()->add_type(name, NULL, type, location); if (!this->in_global_scope() && no->is_type()) - no->type_value()->set_in_function(this->functions_.back().function); + { + Named_object* f = this->functions_.back().function; + unsigned int index; + if (f->is_function()) + index = f->func_value()->new_local_type_index(); + else + index = 0; + no->type_value()->set_in_function(f, index); + } } // Add a named type. @@ -1025,7 +1086,12 @@ Gogo::declare_type(const std::string& na if (!this->in_global_scope() && no->is_type_declaration()) { Named_object* f = this->functions_.back().function; - no->type_declaration_value()->set_in_function(f); + unsigned int index; + if (f->is_function()) + index = f->func_value()->new_local_type_index(); + else + index = 0; + no->type_declaration_value()->set_in_function(f, index); } return no; } @@ -2989,9 +3055,10 @@ Gogo::convert_named_types_in_bindings(Bi Function::Function(Function_type* type, Function* enclosing, Block* block, Location location) : type_(type), enclosing_(enclosing), results_(NULL), - closure_var_(NULL), block_(block), location_(location), fndecl_(NULL), - defer_stack_(NULL), results_are_named_(false), calls_recover_(false), - is_recover_thunk_(false), has_recover_thunk_(false) + closure_var_(NULL), block_(block), location_(location), labels_(), + local_type_count_(0), fndecl_(NULL), defer_stack_(NULL), + results_are_named_(false), calls_recover_(false), is_recover_thunk_(false), + has_recover_thunk_(false) { } @@ -4599,9 +4666,10 @@ Named_object::set_type_value(Named_type* go_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION); Type_declaration* td = this->u_.type_declaration; td->define_methods(named_type); - Named_object* in_function = td->in_function(); + unsigned int index; + Named_object* in_function = td->in_function(&index); if (in_function != NULL) - named_type->set_in_function(in_function); + named_type->set_in_function(in_function, index); delete td; this->classification_ = NAMED_OBJECT_TYPE; this->u_.type_value = named_type; Index: gcc/go/gofrontend/gogo.h =================================================================== --- gcc/go/gofrontend/gogo.h (revision 190405) +++ gcc/go/gofrontend/gogo.h (working copy) @@ -963,6 +963,11 @@ class Function void check_labels() const; + // Note that a new local type has been added. Return its index. + unsigned int + new_local_type_index() + { return this->local_type_count_++; } + // Whether this function calls the predeclared recover function. bool calls_recover() const @@ -1084,6 +1089,8 @@ class Function Location location_; // Labels defined or referenced in the function. Labels labels_; + // The number of local types defined in this function. + unsigned int local_type_count_; // The function decl. tree fndecl_; // The defer stack variable. A pointer to this variable is used to @@ -1638,8 +1645,8 @@ class Type_declaration { public: Type_declaration(Location location) - : location_(location), in_function_(NULL), methods_(), - issued_warning_(false) + : location_(location), in_function_(NULL), in_function_index_(0), + methods_(), issued_warning_(false) { } // Return the location. @@ -1650,13 +1657,19 @@ class Type_declaration // Return the function in which this type is declared. This will // return NULL for a type declared in global scope. Named_object* - in_function() - { return this->in_function_; } + in_function(unsigned int* pindex) + { + *pindex = this->in_function_index_; + return this->in_function_; + } // Set the function in which this type is declared. void - set_in_function(Named_object* f) - { this->in_function_ = f; } + set_in_function(Named_object* f, unsigned int index) + { + this->in_function_ = f; + this->in_function_index_ = index; + } // Add a method to this type. This is used when methods are defined // before the type. @@ -1689,6 +1702,8 @@ class Type_declaration // If this type is declared in a function, a pointer back to the // function in which it is defined. Named_object* in_function_; + // The index of this type in IN_FUNCTION_. + unsigned int in_function_index_; // Methods defined before the type is defined. Methods methods_; // True if we have issued a warning about a use of this type Index: gcc/go/gofrontend/types.h =================================================================== --- gcc/go/gofrontend/types.h (revision 190405) +++ gcc/go/gofrontend/types.h (working copy) @@ -2623,8 +2623,8 @@ class Named_type : public Type public: Named_type(Named_object* named_object, Type* type, Location location) : Type(TYPE_NAMED), - named_object_(named_object), in_function_(NULL), type_(type), - local_methods_(NULL), all_methods_(NULL), + named_object_(named_object), in_function_(NULL), in_function_index_(0), + type_(type), local_methods_(NULL), all_methods_(NULL), interface_method_tables_(NULL), pointer_interface_method_tables_(NULL), location_(location), named_btype_(NULL), dependencies_(), is_visible_(true), is_error_(false), is_placeholder_(false), @@ -2651,13 +2651,19 @@ class Named_type : public Type // Return the function in which this type is defined. This will // return NULL for a type defined in global scope. const Named_object* - in_function() const - { return this->in_function_; } + in_function(unsigned int *pindex) const + { + *pindex = this->in_function_index_; + return this->in_function_; + } // Set the function in which this type is defined. void - set_in_function(Named_object* f) - { this->in_function_ = f; } + set_in_function(Named_object* f, unsigned int index) + { + this->in_function_ = f; + this->in_function_index_ = index; + } // Return the name of the type. const std::string& @@ -2865,6 +2871,8 @@ class Named_type : public Type // If this type is defined in a function, a pointer back to the // function in which it is defined. Named_object* in_function_; + // The index of this type in IN_FUNCTION_. + unsigned int in_function_index_; // The actual type. Type* type_; // The list of methods defined for this type. Any named type can Index: gcc/go/gofrontend/parse.cc =================================================================== --- gcc/go/gofrontend/parse.cc (revision 190405) +++ gcc/go/gofrontend/parse.cc (working copy) @@ -5337,7 +5337,8 @@ Parse::import_spec(void*) if (!token->is_string()) { - error_at(this->location(), "missing import package name"); + error_at(this->location(), "import statement not a string"); + this->advance_token(); return; } Index: gcc/go/gofrontend/gogo-tree.cc =================================================================== --- gcc/go/gofrontend/gogo-tree.cc (revision 190405) +++ gcc/go/gofrontend/gogo-tree.cc (working copy) @@ -1002,9 +1002,19 @@ Named_object::get_id(Gogo* gogo) } if (this->is_type()) { - const Named_object* in_function = this->type_value()->in_function(); + unsigned int index; + const Named_object* in_function = this->type_value()->in_function(&index); if (in_function != NULL) - decl_name += '$' + Gogo::unpack_hidden_name(in_function->name()); + { + decl_name += '$' + Gogo::unpack_hidden_name(in_function->name()); + if (index > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", index); + decl_name += '$'; + decl_name += buf; + } + } } return get_identifier_from_string(decl_name); } Index: gcc/go/gofrontend/lex.cc =================================================================== --- gcc/go/gofrontend/lex.cc (revision 190405) +++ gcc/go/gofrontend/lex.cc (working copy) @@ -722,7 +722,16 @@ Lex::next_token() unsigned int ci; bool issued_error; this->lineoff_ = p - this->linebuf_; - this->advance_one_utf8_char(p, &ci, &issued_error); + const char *pnext = this->advance_one_utf8_char(p, &ci, + &issued_error); + + // Ignore byte order mark at start of file. + if (ci == 0xfeff) + { + p = pnext; + break; + } + if (Lex::is_unicode_letter(ci)) return this->gather_identifier(); @@ -831,6 +840,14 @@ Lex::advance_one_utf8_char(const char* p *issued_error = true; return p + 1; } + + // Warn about byte order mark, except at start of file. + if (*value == 0xfeff && (this->lineno_ != 1 || this->lineoff_ != 0)) + { + error_at(this->location(), "Unicode (UTF-8) BOM in middle of file"); + *issued_error = true; + } + return p + adv; } @@ -1705,6 +1722,27 @@ struct Unicode_range unsigned int stride; }; +// A table of whitespace characters--Unicode code points classified as +// "Space", "C" locale whitespace characters, the "next line" control +// character (0085), the line separator (2028), the paragraph +// separator (2029), and the "zero-width non-break space" (feff). + +static const Unicode_range unicode_space[] = +{ + { 0x0009, 0x000d, 1 }, + { 0x0020, 0x0020, 1 }, + { 0x0085, 0x0085, 1 }, + { 0x00a0, 0x00a0, 1 }, + { 0x1680, 0x1680, 1 }, + { 0x180e, 0x180e, 1 }, + { 0x2000, 0x200a, 1 }, + { 0x2028, 0x2029, 1 }, + { 0x202f, 0x202f, 1 }, + { 0x205f, 0x205f, 1 }, + { 0x3000, 0x3000, 1 }, + { 0xfeff, 0xfeff, 1 }, +}; + // A table of Unicode digits--Unicode code points classified as // "Digit". @@ -2294,6 +2332,15 @@ Lex::is_in_unicode_range(unsigned int c, } } +// Return whether C is a space character. + +bool +Lex::is_unicode_space(unsigned int c) +{ + return Lex::is_in_unicode_range(c, unicode_space, + ARRAY_SIZE(unicode_space)); +} + // Return whether C is a Unicode digit--a Unicode code point // classified as "Digit". Index: gcc/go/gofrontend/types.cc =================================================================== --- gcc/go/gofrontend/types.cc (revision 190405) +++ gcc/go/gofrontend/types.cc (working copy) @@ -588,6 +588,9 @@ Type::are_compatible_for_comparison(bool p != fields->end(); ++p) { + if (Gogo::is_sink_name(p->field_name())) + continue; + if (!p->type()->is_comparable()) { if (reason != NULL) @@ -1295,7 +1298,8 @@ Type::type_descriptor_var_name(Gogo* gog return "__go_td_" + this->mangled_name(gogo); Named_object* no = nt->named_object(); - const Named_object* in_function = nt->in_function(); + unsigned int index; + const Named_object* in_function = nt->in_function(&index); std::string ret = "__go_tdn_"; if (nt->is_builtin()) go_assert(in_function == NULL); @@ -1310,6 +1314,13 @@ Type::type_descriptor_var_name(Gogo* gog { ret.append(Gogo::unpack_hidden_name(in_function->name())); ret.append(1, '.'); + if (index > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", index); + ret.append(buf); + ret.append(1, '.'); + } } } @@ -1746,9 +1757,19 @@ Type::specific_type_functions(Gogo* gogo { // This name is already hidden or not as appropriate. base_name = name->name(); - const Named_object* in_function = name->in_function(); + unsigned int index; + const Named_object* in_function = name->in_function(&index); if (in_function != NULL) - base_name += '$' + Gogo::unpack_hidden_name(in_function->name()); + { + base_name += '$' + Gogo::unpack_hidden_name(in_function->name()); + if (index > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", index); + base_name += '$'; + base_name += buf; + } + } } std::string hash_name = base_name + "$hash"; std::string equal_name = base_name + "$equal"; @@ -1989,10 +2010,19 @@ Type::uncommon_type_constructor(Gogo* go ? gogo->pkgpath() : package->pkgpath()); n.assign(pkgpath); - if (name->in_function() != NULL) + unsigned int index; + const Named_object* in_function = name->in_function(&index); + if (in_function != NULL) { n.append(1, '.'); - n.append(Gogo::unpack_hidden_name(name->in_function()->name())); + n.append(Gogo::unpack_hidden_name(in_function->name())); + if (index > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", index); + n.append(1, '.'); + n.append(buf); + } } s = Expression::make_string(n, bloc); vals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); @@ -4276,6 +4306,9 @@ Struct_type::do_compare_is_identity(Gogo pf != fields->end(); ++pf) { + if (Gogo::is_sink_name(pf->field_name())) + return false; + if (!pf->type()->compare_is_identity(gogo)) return false; @@ -4749,6 +4782,9 @@ Struct_type::write_hash_function(Gogo* g pf != fields->end(); ++pf) { + if (Gogo::is_sink_name(pf->field_name())) + continue; + if (first) first = false; else @@ -4840,6 +4876,9 @@ Struct_type::write_equal_function(Gogo* pf != fields->end(); ++pf, ++field_index) { + if (Gogo::is_sink_name(pf->field_name())) + continue; + // Compare one field in both P1 and P2. Expression* f1 = Expression::make_temporary_reference(p1, bloc); f1 = Expression::make_unary(OPERATOR_MULT, f1, bloc); @@ -8360,6 +8399,13 @@ Named_type::do_reflection(Gogo* gogo, st { ret->append(Gogo::unpack_hidden_name(this->in_function_->name())); ret->push_back('$'); + if (this->in_function_index_ > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", this->in_function_index_); + ret->append(buf); + ret->push_back('$'); + } } ret->append(Gogo::unpack_hidden_name(this->named_object_->name())); } @@ -8389,6 +8435,13 @@ Named_type::do_mangled_name(Gogo* gogo, { name.append(Gogo::unpack_hidden_name(this->in_function_->name())); name.append(1, '$'); + if (this->in_function_index_ > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "%u", this->in_function_index_); + name.append(buf); + name.append(1, '$'); + } } } name.append(Gogo::unpack_hidden_name(no->name())); Index: gcc/go/gofrontend/expressions.cc =================================================================== --- gcc/go/gofrontend/expressions.cc (revision 190613) +++ gcc/go/gofrontend/expressions.cc (working copy) @@ -5184,6 +5184,9 @@ Binary_expression::lower_struct_comparis pf != fields->end(); ++pf, ++field_index) { + if (Gogo::is_sink_name(pf->field_name())) + continue; + if (field_index > 0) { if (left_temp == NULL) Index: gcc/go/gofrontend/lex.h =================================================================== --- gcc/go/gofrontend/lex.h (revision 190405) +++ gcc/go/gofrontend/lex.h (working copy) @@ -375,6 +375,10 @@ class Lex static int fetch_char(const char* str, unsigned int *value); + // Return whether C is a Unicode or "C" locale space character. + static bool + is_unicode_space(unsigned int c); + private: ssize_t get_line();