Firstly, I don't think we need this in base and I think there is little
to no chance of it being taken, even if the code is improved.

Secondly:

- The code is still miles off style(9) and isn't really a consistent
  style within itself either.

- Forking uname(1)? What? No offence, but that is hilarious :-). Why
  fork uname(1) for uname(3) but not date(1) for gettimeofday(2)?

- Why would you fork sed either?

I think C is the wrong tool for this. Why not write a shell, perl, or
python script?

Then if people start to use it you could make a port.



On Fri, Jan 29, 2016 at 01:34:30AM -0600, Luke Small wrote:
> I think I fixed all your suggestions. I don't strictly adhere to kernel
> normal in the use of comments and I parse command-line arguments without
> using getopt(3), but the method is robust.
> 
> -Luke
> 
> <A few quick comments from glancing over this:
> 
>  o I definitely don't think camel case will be accepted
> 
>  o I'm pretty sure strtonum(3) is strongly preferred over strtod(3) et al.

> /*
>  * Copyright (c) 2016 Luke N. Small
>  *
>  * Permission to use, copy, modify, and distribute this software for any
>  * purpose with or without fee is hereby granted, provided that the above
>  * copyright notice and this permission notice appear in all copies.
>  *
>  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
>  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
>  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
>  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
>  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
>  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
>  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>  */
>  
> 
> /* Special thanks to Dan Mclaughlin for the ftp to sed idea
>  * 
>  * ftp -o - http://www.openbsd.org/ftp.html | \
>  * sed -n \
>  *  -e 's:</a>$::' \
>  *    -e 's:  <strong>\([^<]*\)<.*:\1:p' \
>  *    -e 's:^\(       [hfr].*\):\1:p'
>  */
>  
>  
> #define EVENT_NOPOLL
> #define EVENT_NOSELECT
> 
> #include <stdio.h>
> #include <unistd.h>
> #include <stdlib.h>
> #include <err.h>
> #include <errno.h>
> #include <sys/types.h>
> #include <sys/event.h>
> #include <signal.h>
> #include <string.h>
> 
> struct mirror_st
> {
>       char * country_title;
>       char * mirror;
>       char * install_path;
>       double diff;
>       struct mirror_st * next;
> };
> 
> int ftp_cmp (const void * a, const void * b)
> {
>       struct mirror_st ** one = (struct mirror_st **)a;
>       struct mirror_st ** two = (struct mirror_st **)b;
> 
>       if ( (*one)->diff < (*two)->diff )
>               return -1;
>       if ( (*one)->diff > (*two)->diff )
>               return 1;
>       return 0;
> }
> 
> int country_cmp (const void * a, const void * b)
> {
>       struct mirror_st ** one = (struct mirror_st **)a;
>       struct mirror_st ** two = (struct mirror_st **)b;
>       
>       // list the USA mirrors first, it will subsort correctly
>       int8_t temp = !strncmp("USA", (*one)->country_title, 3);
>       if (temp != !strncmp("USA", (*two)->country_title, 3))
>       {
>               if (temp)
>                       return -1;
>               return 1;
>       }
> 
>       return strcmp( (*one)->country_title, (*two)->country_title ) ;
> }
> 
> 
> double get_time_diff(struct timeval a, struct timeval b)
> {
>     long sec;
>     long usec;
>     sec = b.tv_sec - a.tv_sec;
>     usec = b.tv_usec - a.tv_usec;
>     if (usec < 0)
>     {
>               --sec;
>               usec += 1000000;
>     }
>     return sec + ((double)usec / 1000000.0);
> }
> 
> void manpage(char * a)
> {
>       errx(1, "%s [-s timeout] [-n maximum_mirrors_written]", a);
> }
> 
> 
> int main(int argc, char *argv[])
> {
>       pid_t ftp_pid, sed_pid, uname_pid;
>       int ftp_to_sed[2];
>       int sed_to_parent[2];
>       int uname_to_parent[2];
>       char uname_r[5], uname_m[20], character;
>       int i;
>       double s = 7;
>       int position, num, c, n = 5000;
>       FILE *input;
> 
>       if (argc > 1)
>       {
>               if (argc % 2 == 0)
>                       manpage(argv[0]);
>               
>               position = 0;
>               while (++position < argc)
>               {
>                       if (strlen(argv[position]) != 2)
>                               manpage(argv[0]);
>                       
>                       if (!strcmp(argv[position], "-s"))
>                       {
>                               ++position;
>                               c = -1;
>                               i = 0;
>                               while ((character = argv[position][++c]) != 
> '\0')
>                               {
>                                       if (character == '.')
>                                               ++i;
>                                       
>                                       if ( ((character < '0' || character > 
> '9') && character != '.') || i > 1 )
>                                       {
>                                               if (character == '-')
>                                                       errx(1, "No negative 
> numbers.");
>                                               errx(1, "Incorrect floating 
> point format.");
>                                       }
>                               }
>                               errno = 0;
>                               strtod(argv[position], NULL);
>                               if (errno == ERANGE)
>                                       err(1, NULL);
>                               if ((s = strtod(argv[position], NULL)) > 
> 100000000.0)
>                                       errx(1, "The argument should less than 
> or equal to 100000000");
>                       }
>                       else if (!strcmp(argv[position], "-n"))
>                       {
>                               ++position;
>                               if (strlen(argv[position]) > 3)
>                                       errx(1, "Integer should be less than or 
> equal to 3 digits long.");
>                               c = -1;
>                               n = 0;
>                               while ((character = argv[position][++c]) != 
> '\0')
>                               {
>                                       if ( character < '0' || character > '9' 
> )
>                                       {
>                                               if (character == '.')
>                                                       errx(1, "No decimal 
> points.");
>                                               if (character == '-')
>                                                       errx(1, "No negative 
> numbers.");
>                                               errx(1, "Incorrect integer 
> format.");
>                                       }
>                                       n = n * 10 + (int)(character - '0');
>                               }
>                       }
>                       else
>                               manpage(argv[0]);                               
>               }
>       }
> 
>       struct kevent ke[1];
>       struct timespec timeout;
>       
>       timeout.tv_sec = (int)s;
>       timeout.tv_nsec = (int)(   (s - (double)timeout.tv_sec) * 1000000000  );
> 
>       int kq = kqueue();
>       if (kq == -1)
>               errx(1, "kq!");
> 
>       if (pipe(uname_to_parent) == -1)
>               err(1, NULL);
>       
> 
>       // "uname -rm" returns version and architecture like: "5.8 amd64\n" to 
> standard out
>       uname_pid = fork();
>       if (uname_pid == (pid_t) 0)
>       {                                                       /* uname child 
> */
>               close(uname_to_parent[0]);
>               dup2(uname_to_parent[1], STDOUT_FILENO);         /*attaching to 
> pipe(s)*/
>               execl("/usr/bin/uname","/usr/bin/uname", "-rm", NULL);
>         errx(1, "uname execl() failed.");
>       }
>       if (uname_pid == -1)
>               err(1, NULL);
>       
>       close(uname_to_parent[1]);
> 
> 
>       EV_SET(ke, uname_to_parent[0], EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 
> NULL);
>       if (kevent(kq, ke, 1, NULL, 0, NULL) == -1)
>       {
>               kill(uname_pid, SIGKILL);
>               errx(1, "kevent register fail.");
>       }
> 
>       i = kevent(kq, NULL, 0, ke, 1, &timeout);
>       if (i == -1)
>       {
>               kill(uname_pid, SIGKILL);
>               errx(1, "kevent uname");
>       }
>               
>       if (i == 0)
>       {
>               kill(uname_pid, SIGKILL);
>               errx(1, "uname timed out.");
>       }
> 
>       input = fdopen (uname_to_parent[0], "r");
>       if (input == NULL)
>               err(1, NULL);
> 
>       num = 0;
>       position = -1;
>       while ((c = getc(input)) != EOF)
>       {
>               if (num == 0)
>               {
>                       if (++position >= 5)
>                               errx(1, "uname_r[] got too long!");
>                       if (c != ' ')
>                               uname_r[position] = c;
>                       else
>                       {
>                               uname_r[position] = '\0';
>                               num = 1;
>                               position = -1;
>                       }
>               }
>               else
>               {
>                       if (++position >= 20)
>                               errx(1, "uname_m[] got too long!");
>                       if (c != '\n')
>                               uname_m[position] = c;
>                       else
>                       {
>                               uname_m[position] = '\0';
>                               break;
>                       }
>               }
>       }
>       fclose (input);
>       close(uname_to_parent[0]);
> 
> 
>       if (pipe(ftp_to_sed) == -1)
>               err(1, NULL);
> 
>               
>       ftp_pid = fork();
>       if (ftp_pid == (pid_t) 0)
>       {                                                       /*ftp child*/
>               close(ftp_to_sed[0]);
>               dup2(ftp_to_sed[1], STDOUT_FILENO);         /*attaching to 
> pipe(s)*/
>         execl("/usr/bin/ftp","ftp", "-Vo", "-", 
> "http://www.openbsd.org/ftp.html";, NULL);
>         errx(1, "ftp execl() failed.");
>       }
>       if (ftp_pid == -1)
>               err(1, NULL);
> 
>       close(ftp_to_sed[1]);
> 
>       if (pipe(sed_to_parent) == -1)
>               err(1, NULL);
>       
> 
>       sed_pid = fork();
>       if (sed_pid == (pid_t) 0)
>       {                                                       /* sed child */
>               close(sed_to_parent[0]);
>               dup2(ftp_to_sed[0], STDIN_FILENO);         /*attaching to 
> pipe(s)*/
>               dup2(sed_to_parent[1], STDOUT_FILENO);
>               execl("/usr/bin/sed","sed","-n",
>                                                                  "-e", 
> "s:</a>$::",
>                                                                  "-e", 
> "s:\t<strong>\\([^<]*\\)<.*:\\1:p",
>                                                                  "-e", 
> "s:^\\(\t[hfr].*\\):\\1:p", NULL);
>               kill(ftp_pid, SIGKILL);
>         errx(1, "sed execl() failed.");
>       }
>       if (sed_pid == -1)
>       {
>               kill(ftp_pid, SIGKILL);
>               err(1, NULL);
>       }
> 
>       EV_SET(ke, sed_to_parent[0], EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 
> NULL);
>       if (kevent(kq, ke, 1, NULL, 0, NULL) == -1)
>       {
>               kill(ftp_pid, SIGKILL);
>               kill(sed_pid, SIGKILL);
>               errx(1, "sed_to_parent kevent register fail.");
>       }
> 
>       close(ftp_to_sed[0]);         /*close pipe(s) child attached to*/
>       close(sed_to_parent[1]);
> 
>       i = kevent(kq, NULL, 0, ke, 1, &timeout);
>       if (i == -1)
>       {
>               kill(ftp_pid, SIGKILL);
>               kill(sed_pid, SIGKILL);
>               errx(1, "kevent, timeout may be too large");
>       }
>               
>       if (i == 0)
>       {
>               kill(ftp_pid, SIGKILL);
>               kill(sed_pid, SIGKILL);
>               errx(1, "timed out fetching openbsd.org/ftp.html.");
>       }
>     
>       input = fdopen (sed_to_parent[0], "r");
>       if (input == NULL)
>               err(1, NULL);
>       
>       char line[300];
>       num = 0;
>       position = 0;
>       struct mirror_st *start, *end, *mTemp1, *mTemp2, *mTemp3;
>       start = end = malloc(sizeof(struct mirror_st));
>       if (start == NULL)
>               errx(1, "malloc failed.");
>       start->next = NULL;
> 
>       while ((c = getc (input)) != EOF)
>       {
>               if (position >= 300)
>               {
>                       kill(ftp_pid, SIGKILL);
>                       kill(sed_pid, SIGKILL);
>                       errx(1, "line[] got too long!");
>               }
>               if (num == 0)
>               {
>                       if ( c != '\n' )
>                               line[position++] = c;
>                       else
>                       {
>                               // there is a space before the newline that is 
> eliminated
>                               line[position++] = '\0';
>                               
>                               end->country_title = malloc(position);
>                               if (end->country_title == NULL)
>                               {
>                                       kill(ftp_pid, SIGKILL);
>                                       kill(sed_pid, SIGKILL);
>                                       errx(1, "malloc failed.");
>                               }
> 
>                               strlcpy(end->country_title, line, position);
>                               
>                               position = 0;
>                               num = 1;
>                       }               
>               }
>               else
>               {
>                       if (position == 0)
>                       {
>                               if ( (c != 'h') && (c != 'f') && (c != 'r') )
>                                       continue;
>                               else if (c == 'r')
>                                       break;
>                               else if (c == 'f')
>                               {
>                                       // changes ftp listings to http. 
> ftp.html says they can be either
>                                       line[position++] = 'h';
>                                       line[position++] = 't';
>                                       continue;
>                               }
>                       }
>                       if ( c != '\n' )
>                               line[position++] = c;
>                       else
>                       {
>                               line[position++] = '\0';
> 
>                               position += num = strlen(uname_r) + 10 + 
> strlen(uname_m) + 7;
>                               end->mirror = malloc(position);
>                               if (end->mirror == NULL)
>                               {
>                                       kill(ftp_pid, SIGKILL);
>                                       kill(sed_pid, SIGKILL);
>                                       errx(1, "malloc failed.");
>                               }
>                               c =  strlcpy(end->mirror,             line, 
> position);
>                               c += strlcpy(end->mirror + c,       uname_r, 
> position - c);
>                               c += strlcpy(end->mirror + c, "/packages/", 
> position - c);
>                               c += strlcpy(end->mirror + c,       uname_m, 
> position - c);
>                                    strlcpy(end->mirror + c,    "/SHA256", 
> position - c);
> 
>                               position += 15 - num;                           
>                               end->install_path = malloc(position);
>                               if (end->install_path == NULL)
>                               {
>                                       kill(ftp_pid, SIGKILL);
>                                       kill(sed_pid, SIGKILL);
>                                       errx(1, "malloc failed.");
>                               }
>                               c = strlcpy(end->install_path,                  
> line, position);
>                                   strlcpy(end->install_path + c, 
> "%c/packages/%a/", position - c);
>                               
>                               
>                               end = end->next = malloc(sizeof(struct 
> mirror_st));
>                               if (end == NULL)
>                               {
>                                       kill(ftp_pid, SIGKILL);
>                                       kill(sed_pid, SIGKILL);
>                                       errx(1, "malloc failed.");
>                               }
>                               end->next = NULL;
>                               
>                               position = 0;
>                               num = 0;
>                       }                       
>               }
>       }
> 
>       fclose (input);
>       
>       // this is to prevent possible segfault in the next sequence.
>       if (start == end)
>               errx(1, "Too few entries found.");
>       
>       mTemp1 = start;
>       while (mTemp1->next != end)
>               mTemp1 = mTemp1->next;
>       free(end->country_title);
>       free(end);
>       mTemp1->next = NULL;
>       
>       // Eliminate redundant mirrors (no longer need the 'end' value)
>       mTemp1 = start;
>       while (mTemp1->next != NULL)
>       {
>               mTemp2 = mTemp1;
>               do
>               {
>                       if (!strcmp(mTemp1->mirror, mTemp2->next->mirror))
>                       {
>                               mTemp3 = mTemp2->next;
>                               mTemp2 = mTemp2->next = mTemp3->next;
>                               
>                               free(mTemp3->country_title);
>                               free(mTemp3->install_path);
>                               free(mTemp3->mirror);
>                               free(mTemp3);
>                               
>                               if (mTemp2 == NULL)
>                                       break;
>                       }
>                       else
>                               mTemp2 = mTemp2->next;
>                               
>               } while ( mTemp2->next != NULL );
>               mTemp1 = mTemp1->next;
>       }
>       
>       int mirror_num = 0;
>       mTemp1 = start;
>       while (mTemp1 != NULL)
>       {
>               ++mirror_num;
>               mTemp1 = mTemp1->next;
>       }
> 
>       struct mirror_st ** array;
> 
>       if ( (array = calloc(mirror_num, sizeof(struct mirror_st *))) == NULL)
>               errx(1, "calloc failed.");
> 
>       mTemp1 = start;
>       for (c = 0; c < mirror_num; ++c)
>       {
>               array[c] = mTemp1;
>               
>               mTemp1 = mTemp1->next;
>       }
>       
>       qsort(array, mirror_num, sizeof(struct mirror_st *), country_cmp);
> 
>       close(sed_to_parent[0]);
> 
>       for (c = 0; c < mirror_num; ++c)
>       {
>               mTemp1 = array[c];
> 
>               printf("\n%d : %s  :  %s\n", (mirror_num - c) - 1, 
> mTemp1->country_title, mTemp1->mirror);
> 
>               ftp_pid = fork();
>               if (ftp_pid == (pid_t) 0)
>               {                                                       /* ping 
> child */
>                       execl("/usr/bin/ftp", "ftp", "-Vmo", "/dev/null", 
> mTemp1->mirror, NULL);
>                       errx(1, "ftp execl() failed.");
>               }
>               if (ftp_pid == -1)
>                       err(1, NULL);
>               EV_SET(ke, ftp_pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, 
> NOTE_EXIT, 0, NULL);
>               if (kevent(kq, ke, 1, NULL, 0, NULL) == -1)
>               {
>                       kill(ftp_pid, SIGKILL);
>                       errx(1, "kevent register fail.");
>               }
> 
>               mTemp1->diff = 0;
>               struct timeval tv_start, tv_end;
>               gettimeofday(&tv_start, NULL);
> 
>               skip:
> 
>               i = kevent(kq, NULL, 0, ke, 1, &timeout);
>               if (i == -1)
>               {
>                       kill(ftp_pid, SIGKILL);
>                       errx(1, "kevent");
>               }
>               if (i == 0)
>               {
>                       printf("\n");
>                       kill(ftp_pid, SIGKILL);
>                       mTemp1->diff = timeout.tv_sec + (double)timeout.tv_nsec 
> / 1000000000.0;
>                       
>                       // Loop until ftp() is dead.
>                       goto skip;
>               }
> 
>               if ( ke->data == 0 )
>               {
>                       gettimeofday(&tv_end, NULL);
> 
>                       mTemp1->diff = get_time_diff(tv_start, tv_end);
>               }
>               else if ( mTemp1->diff == 0 )
>                       mTemp1->diff  =  timeout.tv_sec + 
> (double)timeout.tv_nsec / 1000000000.0 + 1;
>       }
> 
>       qsort(array, mirror_num, sizeof(struct mirror_st *), ftp_cmp);
> 
>       printf("\n\n");
> 
>       for (c = mirror_num - 1; c >= 0; --c)
>       {
>               printf("%d : %s:\n\t%s : ", c, array[c]->country_title, 
> array[c]->install_path);
>               
>               if (array[c]->diff < timeout.tv_sec + (double)timeout.tv_nsec / 
> 1000000000.0)
>                       printf("%f\n\n", array[c]->diff);
>               else if (array[c]->diff == timeout.tv_sec + 
> (double)timeout.tv_nsec / 1000000000.0)
>                       printf("Timeout\n\n");
>               else
>                       printf("Download Error\n\n");
>       }
>       
>       if (array[0]->diff >= timeout.tv_sec + (double)timeout.tv_nsec / 
> 1000000000.0)
>               errx(1, "No mirrors found within timeout period.");
> 
>       char *buf;
>       int lines;
>       int total_length = 0;
>       int copy = 0;
> 
>       for (position = 0; position < mirror_num; ++position)
>               total_length += strlen("installpath += \n") + 
> strlen(array[position]->install_path);
> 
>       if ( (input = fopen("/etc/pkg.conf", "r")) == NULL )
>       {
>               buf = (char*)malloc(total_length + 1);
>               if (buf == NULL)
>                       errx(1, "malloc failed.");
>       }
>       else
>       {
>               fseek(input, 0, SEEK_END);      
>               num = ftell(input);
>               fseek(input, 0, SEEK_SET);      
>               lines = 0;
>               buf = (char*)malloc(num + total_length + 11 + 1);
>               if (buf == NULL)
>                       errx(1, "malloc failed.");
>               fread(buf, 1, num, input);
>               fclose(input);
>               for (c = 0; c < num; ++c)
>               {
>                       if (buf[c] == '\n')
>                               ++lines;
>               }
> 
>               position = 0;
>               for (c = 0; c < num; ++c)
>               {
>                       if (buf[c] == '\n')
>                       {
>                               if (--lines == 0 && position == 0)
>                                       break;          // get rid of the 
> lonely newline at the end, if it exists
>                               position = 0;
>                       }
>                       else if (position++ == 0)
>                       {
>                               if ( !strncmp(buf + c, "installpath", 11) )
>                               {
>                                       if (lines)
>                                       {
>                                               while (buf[++c] != '\n');
>                                               --lines;
>                                               position = 0;
>                                               continue;
>                                       }
>                                       else
>                                               break;
>                               }
>                       }
>                       buf[copy++] = buf[c];
>               }
>               total_length += num;    
>       }
> 
>       copy += strlcpy(buf + copy,      "installpath = ", total_length - copy);
>       copy += strlcpy(buf + copy, array[0]->install_path, total_length - 
> copy);
> 
>       if (n < mirror_num)
>               mirror_num = n;
> 
>       position = 1;
>       while (position < mirror_num)
>       {
>               // Eliminates dowload error and timeout mirrors
>               if (array[position]->diff >= timeout.tv_sec + 
> (double)timeout.tv_nsec / 1000000000.0)
>                       break;
>               copy += strlcpy(buf + copy,            "\ninstallpath += ", 
> total_length - copy);
>               copy += strlcpy(buf + copy, array[position++]->install_path, 
> total_length - copy);
>       }
>       copy += strlcpy(buf + copy, "\n", total_length - copy);
> 
>       
>       input = fopen("/etc/pkg.conf", "w");
>       if (input != NULL)
>       {
>               fwrite(buf, 1, copy, input);
>               fclose(input);
>               printf("\n\nEdit out all PKG_PATH environment variable 
> exports");
>               printf(" and run \"unset PKG_PATH\".\n\n/etc/pkg.conf:\n%s\n", 
> buf);
>       }
>       else
>       {
>               printf("\n\nThis could have been the contents of /etc/pkg.conf 
> (run as superuser):\n");
>               printf("%s\n", buf);
>       }
>       
>       if (argc == 1)
>               printf("%s [-s timeout] [-n maximum_mirrors_written]\n", 
> argv[0]);
> 
>       return 0;
> }

Reply via email to