From patchwork Wed May 31 01:32:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Masatake YAMATO X-Patchwork-Id: 13261308 X-Patchwork-Delegate: plautrba@redhat.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2D86FC77B7A for ; Wed, 31 May 2023 01:33:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231462AbjEaBdW (ORCPT ); Tue, 30 May 2023 21:33:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40832 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229600AbjEaBdV (ORCPT ); Tue, 30 May 2023 21:33:21 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2268CF9 for ; Tue, 30 May 2023 18:32:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1685496755; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=STXLjgF5rJJ/4dh75acSaEa2oIx0a5P3U05iqmckWH0=; b=QxiFUnbZK1pN0vixPbZYhDDV6vi6NC/4UTA93Su4DsVUVcXGZ6gjyGaFM9HAfE2hqyH690 NOPcvlzF2sFHvasftBrmOhWKSvR54N/ivneWqTOOsVCjrWlxw5opQOR0pSLiowfEfrwYpf 8EecEKNYq7gNE5qGN3YQ/dyqcJgeUII= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-173-REn2fJ9VNh-finzt47WByQ-1; Tue, 30 May 2023 21:32:33 -0400 X-MC-Unique: REn2fJ9VNh-finzt47WByQ-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id BF33B85A5B5 for ; Wed, 31 May 2023 01:32:32 +0000 (UTC) Received: from dev64.localdomain.com (unknown [10.64.240.15]) by smtp.corp.redhat.com (Postfix) with ESMTP id 178D0140EBB8; Wed, 31 May 2023 01:32:31 +0000 (UTC) From: Masatake YAMATO To: selinux@vger.kernel.org Cc: yamato@redhat.com Subject: [PATCH 1/4] dismod: add --help option Date: Wed, 31 May 2023 10:32:21 +0900 Message-Id: <20230531013224.1135775-1-yamato@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.7 Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org Signed-off-by: Masatake YAMATO Acked-by: James Carter --- checkpolicy/test/dismod.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c index 929ee308..eb090a36 100644 --- a/checkpolicy/test/dismod.c +++ b/checkpolicy/test/dismod.c @@ -66,7 +66,11 @@ static const char *symbol_labels[9] = { static __attribute__((__noreturn__)) void usage(const char *progname) { - printf("usage: %s binary_pol_file\n\n", progname); + puts("Usage:"); + printf(" %s [OPTIONS] binary_pol_file\n\n", progname); + puts("Options:"); + puts(" -h, --help print this help message"); + puts("\n"); exit(1); } @@ -872,7 +876,7 @@ int main(int argc, char **argv) FILE *out_fp = stdout; char ans[81], OutfileName[121]; - if (argc != 2) + if (argc < 2 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) usage(argv[0]); /* read the binary policy */ From patchwork Wed May 31 01:32:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Masatake YAMATO X-Patchwork-Id: 13261309 X-Patchwork-Delegate: plautrba@redhat.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DC3E6C7EE23 for ; Wed, 31 May 2023 01:33:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233829AbjEaBdX (ORCPT ); Tue, 30 May 2023 21:33:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40834 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232025AbjEaBdW (ORCPT ); Tue, 30 May 2023 21:33:22 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id ABAF0EC for ; Tue, 30 May 2023 18:32:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1685496757; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2vDkeU+bLjWmsBkzjdSe+wO+l3/2bG6fd53zNAmYr0s=; b=Cmok18x0GmtDiUdxeTzalVOI96Szoty6IC9y0k2HIiXufuWjV1QuBbdGhZGguQAQN4msc4 5Z2rIOCgv5iFA7Gxc81ov8y09XpoV+Gfx46lghZVW0eNTCpDB2MgexxT8D/bu3I8N2/vCt XF9OqqB8+GA1BPVmNoNCZq4okhUgNFo= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-341-atFJT_tpNOCf72TjhU-PgQ-1; Tue, 30 May 2023 21:32:36 -0400 X-MC-Unique: atFJT_tpNOCf72TjhU-PgQ-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id C827C381494A for ; Wed, 31 May 2023 01:32:35 +0000 (UTC) Received: from dev64.localdomain.com (unknown [10.64.240.15]) by smtp.corp.redhat.com (Postfix) with ESMTP id 20721140E955; Wed, 31 May 2023 01:32:34 +0000 (UTC) From: Masatake YAMATO To: selinux@vger.kernel.org Cc: yamato@redhat.com Subject: [PATCH 2/4] dismod: delete an unnecessary empty line Date: Wed, 31 May 2023 10:32:22 +0900 Message-Id: <20230531013224.1135775-2-yamato@redhat.com> In-Reply-To: <20230531013224.1135775-1-yamato@redhat.com> References: <20230531013224.1135775-1-yamato@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.7 Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org Signed-off-by: Masatake YAMATO --- checkpolicy/test/dismod.c | 1 - 1 file changed, 1 deletion(-) diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c index eb090a36..f1b879b0 100644 --- a/checkpolicy/test/dismod.c +++ b/checkpolicy/test/dismod.c @@ -1,4 +1,3 @@ - /* Authors: Frank Mayer and Karl MacMillan * * Copyright (C) 2003,2004,2005 Tresys Technology, LLC From patchwork Wed May 31 01:32:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Masatake YAMATO X-Patchwork-Id: 13261310 X-Patchwork-Delegate: plautrba@redhat.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AAB10C7EE2C for ; Wed, 31 May 2023 01:33:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232025AbjEaBdX (ORCPT ); Tue, 30 May 2023 21:33:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40872 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229600AbjEaBdW (ORCPT ); Tue, 30 May 2023 21:33:22 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 499D010C for ; Tue, 30 May 2023 18:32:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1685496759; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=oBPuimJWlm9ZdVVc8N1yEv41cmZoFmuJoClvLGn8pqo=; b=GHJubIZp/gO3ZiWuErhcmIMRlDO7RVmCmCIsT8DBVYE4qoQKDNzyjAXX3h4Lwo+i6Wb/KU It+RpESlrWGPZ6tVguhCV5dohnVNgeOQQU71EoKr0RDV+Y6UTHTG3ptoHOSWDTUwyW52tH O3OPsqrAcxQ0LKgMSwGMLN2/iEeyEjU= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-70-c3RiI5TNPiK9uq6BgdN8jw-1; Tue, 30 May 2023 21:32:38 -0400 X-MC-Unique: c3RiI5TNPiK9uq6BgdN8jw-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id C948A101A531 for ; Wed, 31 May 2023 01:32:37 +0000 (UTC) Received: from dev64.localdomain.com (unknown [10.64.240.15]) by smtp.corp.redhat.com (Postfix) with ESMTP id 21657140EBB8; Wed, 31 May 2023 01:32:36 +0000 (UTC) From: Masatake YAMATO To: selinux@vger.kernel.org Cc: yamato@redhat.com Subject: [PATCH 3/4] dismod: handle EOF in user interaction Date: Wed, 31 May 2023 10:32:23 +0900 Message-Id: <20230531013224.1135775-3-yamato@redhat.com> In-Reply-To: <20230531013224.1135775-1-yamato@redhat.com> References: <20230531013224.1135775-1-yamato@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.7 Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org Signed-off-by: Masatake YAMATO --- checkpolicy/test/dismod.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c index f1b879b0..3b81b1ce 100644 --- a/checkpolicy/test/dismod.c +++ b/checkpolicy/test/dismod.c @@ -921,6 +921,8 @@ int main(int argc, char **argv) for (;;) { printf("\nCommand (\'m\' for menu): "); if (fgets(ans, sizeof(ans), stdin) == NULL) { + if (feof(stdin)) + break; fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__, strerror(errno)); continue; From patchwork Wed May 31 01:32:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Masatake YAMATO X-Patchwork-Id: 13261311 X-Patchwork-Delegate: plautrba@redhat.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0F10DC7EE23 for ; Wed, 31 May 2023 01:33:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232828AbjEaBda (ORCPT ); Tue, 30 May 2023 21:33:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40878 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229600AbjEaBd3 (ORCPT ); Tue, 30 May 2023 21:33:29 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D5F7410B for ; Tue, 30 May 2023 18:32:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1685496762; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sOqlpP3mjiZAe/MMxj39LhA3HTeZoIBDXrXmpg4cbL4=; b=dr/0lcYr4hddbPJ2Oe+sdk5yS/MKc61IWu5FPGC0kPRwt6ceNZrr1i4xAEeGlYvzZmod31 FeRz+vG233Lru9shXioculhCDlpdLUlU2jn/mrFIfsxkzVJkAx2D43ulzmBjseVFvmxA3W zOPTD47iTaRNJP02C6jCH5vkc3uGwjA= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-97-LdxDyqsoOUCLUhUy0V0wwA-1; Tue, 30 May 2023 21:32:40 -0400 X-MC-Unique: LdxDyqsoOUCLUhUy0V0wwA-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 31A511C04B45 for ; Wed, 31 May 2023 01:32:40 +0000 (UTC) Received: from dev64.localdomain.com (unknown [10.64.240.15]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7D347140EBB8; Wed, 31 May 2023 01:32:39 +0000 (UTC) From: Masatake YAMATO To: selinux@vger.kernel.org Cc: yamato@redhat.com Subject: [PATCH 4/4] dismod: add --actions option for non-interactive use Date: Wed, 31 May 2023 10:32:24 +0900 Message-Id: <20230531013224.1135775-4-yamato@redhat.com> In-Reply-To: <20230531013224.1135775-1-yamato@redhat.com> References: <20230531013224.1135775-1-yamato@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.7 Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org Example session: $ ./dismod --help Usage: ./dismod [OPTIONS] binary_pol_file Options: -h, --help print this help message -a, --actions ACTIONS run non-interactively Actions: 1 display unconditional AVTAB 2 display conditional AVTAB 3 display users 4 display bools 5 display roles 6 display types, attributes, and aliases 7 display role transitions 8 display role allows 9 Display policycon 0 Display initial SIDs a Display avrule requirements b Display avrule declarations c Display policy capabilities u Display the unknown handling setting F Display filename_trans rules $ ./dismod --actions 16 input.mod Reading policy... libsepol.policydb_index_others: security: 0 users, 1 roles, 2 types, 0 bools libsepol.policydb_index_others: security: 0 sens, 0 cats libsepol.policydb_index_others: security: 1 classes, 0 rules, 0 cond rules libsepol.policydb_index_others: security: 0 users, 1 roles, 2 types, 0 bools libsepol.policydb_index_others: security: 0 sens, 0 cats libsepol.policydb_index_others: security: 1 classes, 0 rules, 0 cond rules Binary policy module file loaded. Module name: input Module version: 1.0.0 Policy version: 21 unconditional avtab: --- begin avrule block --- decl 1: allow [httpd_t] [http_port_t] : [tcp_socket] { name_bind }; [http_port_t] [2]: type flags:0 [httpd_t] [1]: type flags:0 Signed-off-by: Masatake YAMATO --- checkpolicy/test/dismod.c | 115 +++++++++++++++++++++++++++----------- 1 file changed, 83 insertions(+), 32 deletions(-) diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c index 3b81b1ce..515fc9a5 100644 --- a/checkpolicy/test/dismod.c +++ b/checkpolicy/test/dismod.c @@ -63,13 +63,57 @@ static const char *symbol_labels[9] = { "levels ", "cats ", "attribs" }; +static struct command { + enum { + EOL = 0, + HEADER = 1, + CMD = 1 << 1, + NOOPT = 1 << 2, + } meta; + char cmd; + const char *desc; +} commands[] = { + {HEADER, 0, "\nSelect a command:"}, + {CMD, '1', "display unconditional AVTAB" }, + {CMD, '2', "display conditional AVTAB" }, + {CMD, '3', "display users" }, + {CMD, '4', "display bools" }, + {CMD, '5', "display roles" }, + {CMD, '6', "display types, attributes, and aliases" }, + {CMD, '7', "display role transitions" }, + {CMD, '8', "display role allows" }, + {CMD, '9', "Display policycon" }, + {CMD, '0', "Display initial SIDs" }, + {HEADER, 0, ""}, + {CMD, 'a', "Display avrule requirements"}, + {CMD, 'b', "Display avrule declarations"}, + {CMD, 'c', "Display policy capabilities"}, + {CMD|NOOPT, 'l', "Link in a module"}, + {CMD, 'u', "Display the unknown handling setting"}, + {CMD, 'F', "Display filename_trans rules"}, + {HEADER, 0, ""}, + {CMD|NOOPT, 'f', "set output file"}, + {CMD|NOOPT, 'm', "display menu"}, + {CMD|NOOPT, 'q', "quit"}, + {EOL, 0, "" }, +}; + static __attribute__((__noreturn__)) void usage(const char *progname) { puts("Usage:"); printf(" %s [OPTIONS] binary_pol_file\n\n", progname); puts("Options:"); - puts(" -h, --help print this help message"); - puts("\n"); + puts(" -h, --help print this help message"); + puts(" -a, --actions ACTIONS run non-interactively"); + puts(""); + puts("Actions:"); + for (unsigned int i = 0; commands[i].meta != EOL; i++) { + if (commands[i].meta == HEADER + || commands[i].meta & NOOPT) + continue; + printf(" %c %s\n", commands[i].cmd, commands[i].desc); + } + puts(""); exit(1); } @@ -845,46 +889,46 @@ static void display_policycaps(policydb_t * p, FILE * fp) static int menu(void) { - printf("\nSelect a command:\n"); - printf("1) display unconditional AVTAB\n"); - printf("2) display conditional AVTAB\n"); - printf("3) display users\n"); - printf("4) display bools\n"); - printf("5) display roles\n"); - printf("6) display types, attributes, and aliases\n"); - printf("7) display role transitions\n"); - printf("8) display role allows\n"); - printf("9) Display policycon\n"); - printf("0) Display initial SIDs\n"); - printf("\n"); - printf("a) Display avrule requirements\n"); - printf("b) Display avrule declarations\n"); - printf("c) Display policy capabilities\n"); - printf("l) Link in a module\n"); - printf("u) Display the unknown handling setting\n"); - printf("F) Display filename_trans rules\n"); - printf("\n"); - printf("f) set output file\n"); - printf("m) display menu\n"); - printf("q) quit\n"); + unsigned int i; + for (i = 0; commands[i].meta != EOL; i++) { + if (commands[i].meta == HEADER) + printf("%s\n", commands[i].desc); + else if (commands[i].meta & CMD) + printf("%c) %s\n", commands[i].cmd, commands[i].desc); + } return 0; } int main(int argc, char **argv) { + char *ops = NULL; + char *mod; FILE *out_fp = stdout; char ans[81], OutfileName[121]; if (argc < 2 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) usage(argv[0]); + mod = argv[1]; + if (strcmp (mod, "--actions") == 0 || strcmp (mod, "-a") == 0) { + if (argc != 4) { + fprintf(stderr, "%s: unexpected number of arguments\n", argv[0]); + usage(argv[0]); + } + ops = argv[2]; + mod = argv[3]; + } else if (mod[0] == '-') { + fprintf(stderr, "%s: unknown option: %s\n", argv[0], mod); + usage(argv[0]); + } + /* read the binary policy */ fprintf(out_fp, "Reading policy...\n"); if (policydb_init(&policydb)) { fprintf(stderr, "%s: Out of memory!\n", __FUNCTION__); exit(1); } - if (read_policy(argv[1], &policydb)) { + if (read_policy(mod, &policydb)) { fprintf(stderr, "%s: error(s) encountered while loading policy\n", argv[0]); @@ -917,15 +961,22 @@ int main(int argc, char **argv) } printf("Policy version: %d\n\n", policydb.policyvers); - menu(); + if (!ops) + menu(); for (;;) { - printf("\nCommand (\'m\' for menu): "); - if (fgets(ans, sizeof(ans), stdin) == NULL) { - if (feof(stdin)) - break; - fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__, + if (ops) { + puts(""); + ans[0] = *ops? *ops++: 'q'; + ans[1] = '\0'; + } else { + printf("\nCommand (\'m\' for menu): "); + if (fgets(ans, sizeof(ans), stdin) == NULL) { + if (feof(stdin)) + break; + fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__, strerror(errno)); - continue; + continue; + } } switch (ans[0]) {