[RFC/PATCH v2] Implement new '--output-dir' option.
Thadeu Lima de Souza Cascardo
cascardo en cascardo.info
Lun Mar 24 22:47:56 UTC 2014
On Sun, Mar 23, 2014 at 02:19:29AM -0300, Sergio Durigan Junior wrote:
> This commit implements the new '--output-dir' (or '-o') option. It can
> be used to specify where the receipt of the submission will be saved.
>
> Currently, rnetclient saves the receipt in your $HOME, with a generic
> name which is composed using the CPF and some random digits. This is
> not good because the proprietary IRPF program expects the receipt name
> to be the same as the declaration name, but with the extension renamed
> from ".DEC" to ".REC".
>
> This patch implements some new concepts. First, if the user provides an
> output directory in the command line, we save the receipt there. If she
> does not provide anything, then we save the receipt in the current
> working dir (CWD), which is a more sensitive decision IMO. Also, and
> perhaps more important, is the fact that now the program automagically
> detects when the filename has the ".DEC" extension, and uses the same
> filename for the receipt in this case (replacing ".DEC" by ".REC", as
> expected). This makes the proprietary crap recognize our receipt
> out-of-the-box, without having to worry with renamings. It is worth
> mentioning that if the user provides a declaration file which does not
> have the ".DEC" extension, then rnetclient fallbacks to the old behavior
> and saves the filename as "$CPF.REC" (I chose not to use random digits
> in the end of the filename).
>
> On a side note, I would like to say that I am not entirely happy with
> the way we handle missing directories and files, but that is a topic for
> a completely different patch...
> ---
Aplicado.
Cascardo.
> rnetclient.c | 124 ++++++++++++++++++++++++++++++++++++++++-------------------
> 1 file changed, 84 insertions(+), 40 deletions(-)
>
> diff --git a/rnetclient.c b/rnetclient.c
> index 181ac2f..137b271 100644
> --- a/rnetclient.c
> +++ b/rnetclient.c
> @@ -22,6 +22,9 @@
> #include <stdio.h>
> #include <stdlib.h>
> #include <errno.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> #include <unistd.h>
> #include <sys/socket.h>
> #include <netinet/in.h>
> @@ -46,7 +49,7 @@ static const char rnetclient_doc[] =
> "Send the Brazilian Income Tax Report to the Brazilian "
> "Tax Authority";
> static const char rnetclient_args_doc[] =
> - "[-d|--declaration] FILE";
> + "[-d|--declaration] FILE [-o|--output-dir DIRECTORY]";
>
> /* Description and definition of each option accepted by the program. */
>
> @@ -55,12 +58,19 @@ static const struct argp_option rnetclient_options_desc[] = {
> "The Income Tax Report file that will be sent.",
> 0 },
>
> + { "output-dir", 'o', "DIRECTORY", 0,
> + "The directory where you wish to save the receipt.",
> + 0 },
> +
> { NULL },
> };
>
> struct rnetclient_args {
> /* File representing the declaration. */
> char *input_file;
> +
> + /* Output directory to save the receipt. */
> + char *output_dir;
> };
>
> /* Parser for command line arguments. */
> @@ -75,6 +85,10 @@ static error_t rnetclient_parse_opt(int key, char *arg, struct argp_state *state
> a->input_file = arg;
> break;
>
> + case 'o':
> + a->output_dir = arg;
> + break;
> +
> case ARGP_KEY_ARG:
> /* The user has possibly provided a filename without
> using any switches (e.g., by running './rnetclient
> @@ -331,58 +345,88 @@ static int rnet_recv(gnutls_session_t session, struct rnet_message **message)
> return 0;
> }
>
> -static void save_rec_file(char *cpf, char *buffer, int len)
> +static void save_rec_file(char *cpf, char *buffer, int len, const struct rnetclient_args *args)
> {
> int fd;
> - char *filename;
> - char *home, *tmpdir;
> - mode_t mask;
> - size_t fnlen;
> - int r;
> - home = getenv("HOME");
> - if (!home) {
> - tmpdir = getenv("TMPDIR");
> - if (!tmpdir)
> - tmpdir = "/tmp";
> - home = tmpdir;
> + char cwd[PATH_MAX];
> + char *path, *fname, *tmp;
> + size_t fname_len, r;
> + /* If the user provided the output directory where she wishes
> + to save the receipt, then we use it. Otherwise, we save
> + the file in the current working directory (CWD). */
> + if (args->output_dir == NULL)
> + path = getcwd(cwd, PATH_MAX);
> + else {
> + struct stat st;
> + if (stat(args->output_dir, &st) < 0) {
> + fprintf(stderr, "Could not stat directory \"%s\": %s\n", args->output_dir, strerror(errno));
> + return;
> + }
> + if (!S_ISDIR(st.st_mode)) {
> + fprintf(stderr, "Error: \"%s\" is a not a directory.\n", args->output_dir);
> + return;
> + }
> + path = args->output_dir;
> }
> - fnlen = strlen(home) + strlen(cpf) + 13;
> - filename = malloc(fnlen);
> - snprintf(filename, fnlen, "%s/%s.REC.XXXXXX", home, cpf);
> - mask = umask(0177);
> - fd = mkstemp(filename);
> - if (fd < 0) {
> - fprintf(stderr, "Could not create receipt file: %s\n",
> - strerror(errno));
> - goto out;
> + /* Now it's time to decide which filename to write. We use
> + the declaration's filename as a base layout, because the
> + proprietary version of the IRPF program only recognizes
> + receipts if they have the same name as the declaration
> + files (disconsidering the extensions). For example, if the
> + declaration file is named "123.DEC", the receipt should be
> + named "123.REC". Therefore, if the declaration file has
> + the ".DEC" extension, we strip it out and add the ".REC".
> + Otherwise, we use the default template, which is to save
> + the receipt with the name "$CPF.REC". */
> + tmp = strstr(args->input_file, ".DEC");
> + if (tmp != NULL && tmp[sizeof(".DEC") - 1] == '\0') {
> + const char *p;
> + /* We found the ".REC" extension. */
> + p = strdup(args->input_file);
> + /* Replacing the ".DEC" by ".REC". Fortunately, we
> + just have to change one letter. */
> + tmp = strstr(p, ".DEC");
> + tmp[1] = 'R';
> + fname_len = strlen(p) + strlen(path) + 2;
> + fname = alloca(fname_len);
> + snprintf(fname, fname_len, "%s/%s", path, p);
> + } else {
> + /* The declaration filename does not follow the
> + convention, so we will not use it as a template.
> + We just generate a filename using "$CPF.REC". */
> + fname_len = strlen(cpf) + strlen(path) + sizeof(".REC") + 2;
> + fname = alloca(fname_len);
> + snprintf(fname, fname_len, "%s/%s.REC", path, cpf);
> }
> - r = write(fd, buffer, len);
> - if (r != len) {
> - fprintf(stderr, "Could not write to receipt file%s%s\n",
> - r < 0 ? ": " : ".",
> - r < 0 ? strerror(errno) : "");
> - goto out;
> + /* Now, open the file and write. */
> + fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
> + if (fd < 0) {
> + fprintf(stderr, "Could not create receipt file \"%s\": %s\n", fname, strerror(errno));
> + return;
> }
> - fprintf(stderr, "Wrote the receipt to %s.\n", filename);
> -out:
> + do {
> + r = write(fd, buffer, len);
> + } while (r != len && errno == EAGAIN);
> + if (r != len)
> + fprintf(stderr, "Could not write to receipt file: %s", strerror(errno));
> + else
> + fprintf(stderr, "Wrote the receipt file to %s.\n", fname);
> close(fd);
> - free(filename);
> - umask(mask);
> }
>
> -static void handle_response_text_and_file(char *cpf, struct rnet_message *message)
> +static void handle_response_text_and_file(char *cpf, struct rnet_message *message, const struct rnetclient_args *args)
> {
> char *value;
> int vlen;
> if (!rnet_message_parse(message, "texto", &value, &vlen))
> fprintf(stderr, "%.*s\n", vlen, value);
> if (!rnet_message_parse(message, "arquivo", &value, &vlen))
> - save_rec_file(cpf, value, vlen);
> + save_rec_file(cpf, value, vlen, args);
> }
>
> -static void handle_response_already_found(char *cpf, struct rnet_message *message)
> +static void handle_response_already_found(char *cpf, struct rnet_message *message, const struct rnetclient_args *args)
> {
> - handle_response_text_and_file(cpf, message);
> + handle_response_text_and_file(cpf, message, args);
> }
>
> static void handle_response_error(struct rnet_message *message)
> @@ -457,19 +501,19 @@ int main(int argc, char **argv)
> }
> switch (message->buffer[0]) {
> case 1: /* go ahead */
> - handle_response_text_and_file(cpf, message);
> + handle_response_text_and_file(cpf, message, &rnet_args);
> break;
> case 3: /* error */
> handle_response_error(message);
> finish = 1;
> break;
> case 4:
> - handle_response_already_found(cpf, message);
> + handle_response_already_found(cpf, message, &rnet_args);
> finish = 1;
> break;
> case 2:
> case 5:
> - handle_response_text_and_file(cpf, message);
> + handle_response_text_and_file(cpf, message, &rnet_args);
> finish = 1;
> break;
> }
> @@ -495,7 +539,7 @@ int main(int argc, char **argv)
> case 4:
> case 5:
> case 1:
> - handle_response_text_and_file(cpf, message);
> + handle_response_text_and_file(cpf, message, &rnet_args);
> break;
> }
>
> --
> 1.8.1.4
>
>
> --
> Sergio
Más información sobre la lista de distribución Softwares-impostos