"Fossies" - the Fresh Open Source Software Archive

Member "links-1.03/default.c" (23 Nov 2011, 32509 Bytes) of archive /linux/www/links-1.03.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "default.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.8_vs_1.03.

    1 #include "links.h"
    2 
    3 void get_system_name()
    4 {
    5 #if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
    6     struct utsname name;
    7     memset(&name, 0, sizeof name);
    8     if (!uname(&name)) {
    9         unsigned char *str = init_str();
   10         int l = 0;
   11         add_to_str(&str, &l, name.sysname);
   12         add_to_str(&str, &l, " ");
   13         add_to_str(&str, &l, name.release);
   14         add_to_str(&str, &l, " ");
   15         add_to_str(&str, &l, name.machine);
   16         if (l >= MAX_STR_LEN) str[MAX_STR_LEN - 1] = 0;
   17         strcpy(system_name, str);
   18         mem_free(str);
   19         return;
   20     }
   21 #endif
   22 #ifdef HAVE_POPEN
   23     {
   24         FILE *f;
   25         unsigned char *p;
   26         memset(system_name, 0, MAX_STR_LEN);
   27         if (!(f = popen("uname -srm", "r"))) goto fail;
   28         if (fread(system_name, 1, MAX_STR_LEN - 1, f) <= 0) {
   29             pclose(f);
   30             goto fail;
   31         }
   32         pclose(f);
   33         for (p = system_name; *p; p++) if (*p < ' ') {
   34             *p = 0;
   35             break;
   36         }
   37         if (system_name[0]) return;
   38     }
   39     fail:
   40 #endif
   41     strcpy(system_name, SYSTEM_NAME);
   42 }
   43 
   44 extern struct option links_options[];
   45 extern struct option html_options[];
   46 
   47 struct option *all_options[] = { links_options, html_options, NULL, };
   48 
   49 unsigned char *p_arse_options(int argc, unsigned char *argv[], struct option **opt)
   50 {
   51     unsigned char *e, *u = NULL;
   52     while (argc) {
   53         int i;
   54         argv++, argc--;
   55         if (argv[-1][0] == '-') {
   56             struct option *options;
   57             struct option **op;
   58             for (op = opt; (options = *op); op++) for (i = 0; options[i].p; i++)
   59                 if (options[i].rd_cmd && options[i].cmd_name &&
   60                     !strcasecmp(options[i].cmd_name, &argv[-1][1])) {
   61                     if ((e = options[i].rd_cmd(&options[i], &argv, &argc))) {
   62                         if (e[0]) fprintf(stderr, "Error parsing option %s: %s\n", argv[-1], e);
   63                         return NULL;
   64                     }
   65                     goto found;
   66                 }
   67             uu:
   68             fprintf(stderr, "Unknown option %s\n", argv[-1]);
   69             return NULL;
   70         } else if (!u) u = argv[-1];
   71         else goto uu;
   72         found:;
   73     }
   74     if (u) return u;
   75     return "";
   76 }
   77 
   78 unsigned char *parse_options(int argc, unsigned char *argv[])
   79 {
   80     return p_arse_options(argc, argv, all_options);
   81 }
   82 
   83 unsigned char *get_token(unsigned char **line)
   84 {
   85     unsigned char *s = NULL;
   86     int l = 0;
   87     int escape = 0;
   88     int quote = 0;
   89     
   90     while (**line == ' ' || **line == 9) (*line)++;
   91     if (**line) {
   92         for (s = init_str(); **line; (*line)++) {
   93             if (escape) 
   94                 escape = 0;
   95             else if (**line == '\\') {
   96                 escape = 1; 
   97                 continue;
   98             }   
   99             else if (**line == '"') {
  100                 quote = !quote;
  101                     continue;
  102             }
  103             else if ((**line == ' ' || **line == 9) && !quote)
  104                 break;
  105             add_chr_to_str(&s, &l, **line);
  106         }
  107     }
  108     return s;
  109 }
  110 
  111 void parse_config_file(unsigned char *name, unsigned char *file, struct option **opt)
  112 {
  113     struct option *options;
  114     struct option **op;
  115     int err = 0;
  116     int line = 0;
  117     unsigned char *e;
  118     int i;
  119     unsigned char *n, *p;
  120     unsigned char *tok;
  121     int nl, pl;
  122     while (file[0]) {
  123         line++;
  124         while (file[0] && (file[0] == ' ' || file[0] == 9)) file++;
  125         n = file;
  126         while (file[0] && file[0] > ' ') file++;
  127         if (file == n) {
  128             if (file[0]) file++;
  129             continue;
  130         }
  131         nl = file - n;
  132         while (file[0] == 9 || file[0] == ' ') file++;
  133         p = file;
  134         while (file[0] && file[0] != 10 && file[0] != 13) file++;
  135         pl = file - p;
  136         if (file[0]) {
  137             if ((file[1] == 10 || file[1] == 13) && file[0] != file[1]) file++;
  138             file++;
  139         }
  140         tok = NULL;
  141         if (n[0] == '#') goto f;
  142         if (!(tok = get_token(&n))) goto f;
  143         nl = strlen(tok);
  144         for (op = opt; (options = *op); op++) 
  145                 for (i = 0; options[i].p; i++) if (options[i].cfg_name && (size_t)nl == strlen(options[i].cfg_name) && !casecmp(tok, options[i].cfg_name, nl)) {
  146                 unsigned char *o = memacpy(p, pl);
  147                 if ((e = options[i].rd_cfg(&options[i], o))) {
  148                     if (e[0]) fprintf(stderr, "Error parsing config file %s, line %d: %s\n", name, line, e), err = 1;
  149                 }
  150                 mem_free(o);
  151                 goto f;
  152             }
  153         fprintf(stderr, "Unknown option in config file %s, line %d\n", name, line);
  154         err = 1;
  155         f:
  156         if (tok) mem_free(tok);
  157     }
  158     if (err) fprintf(stderr, "\007"), sleep(3);
  159 }
  160 
  161 unsigned char *create_config_string(struct option *options)
  162 {
  163     unsigned char *s = init_str();
  164     int l = 0;
  165     int i;
  166     add_to_str(&s, &l, "# This file is automatically generated by Links -- please do not edit.");
  167     for (i = 0; options[i].p; i++) if (options[i].wr_cfg)
  168         options[i].wr_cfg(&options[i], &s, &l);
  169     add_to_str(&s, &l, NEWLINE);
  170     return s;
  171 }
  172 
  173 #define FILE_BUF    1024
  174 
  175 unsigned char cfg_buffer[FILE_BUF];
  176 
  177 unsigned char *read_config_file(unsigned char *name)
  178 {
  179     int h, r;
  180     int l = 0;
  181     unsigned char *s;
  182     if ((h = open(name, O_RDONLY | O_NOCTTY)) == -1) return NULL;
  183     set_bin(h);
  184     s = init_str();
  185     while ((r = read(h, cfg_buffer, FILE_BUF)) > 0) {
  186         int i;
  187         for (i = 0; i < r; i++) if (!cfg_buffer[i]) cfg_buffer[i] = ' ';
  188         add_bytes_to_str(&s, &l, cfg_buffer, r);
  189     }
  190     if (r == -1) mem_free(s), s = NULL;
  191     close(h);
  192     return s;
  193 }
  194 
  195 int write_to_config_file(unsigned char *name, unsigned char *c)
  196 {
  197     int rr;
  198     int r;
  199     int h, w;
  200     unsigned char *tmp_name = stracpy(name);
  201     unsigned char *ds, *dt, *dot;
  202     for (dt = ds = tmp_name; *dt; dt++) if (dir_sep(*dt)) ds = dt;
  203     if ((dot = strchr(ds, '.'))) *dot = 0;
  204     add_to_strn(&tmp_name, ".tmp");
  205     if ((h = open(tmp_name, O_WRONLY | O_NOCTTY | O_CREAT | O_TRUNC, 0666)) == -1) {
  206         mem_free(tmp_name);
  207         return get_error_from_errno(errno);
  208     }
  209     set_bin(h);
  210     rr = strlen(c);
  211     r = rr;
  212     while (r > 0) {
  213         if ((w = write(h, c + rr - r, r)) <= 0) {
  214             int err = !w ? ENOSPC : errno;
  215             close(h);
  216             unlink(tmp_name);
  217             mem_free(tmp_name);
  218             return get_error_from_errno(err);
  219         }
  220         r -= w;
  221     }
  222     close(h);
  223 #ifndef RENAME_OVER_EXISTING_FILES
  224     unlink(name);
  225 #endif
  226     if (rename(tmp_name, name)) {
  227         int err = errno;
  228         unlink(tmp_name);
  229         mem_free(tmp_name);
  230         return get_error_from_errno(err);
  231     }
  232     mem_free(tmp_name);
  233     return 0;
  234 }
  235 
  236 unsigned char *get_home(int *n)
  237 {
  238     struct stat st;
  239     unsigned char *home = NULL;
  240     unsigned char *home_links;
  241     unsigned char *config_dir = stracpy(getenv("CONFIG_DIR"));
  242 
  243     if (n) *n = 1;
  244 #ifdef WIN32
  245     if (!home) {
  246         home = stracpy(getenv("APPDATA"));
  247 #ifdef HAVE_CYGWIN_CONV_PATH
  248         /*
  249          * Newer Cygwin complains about windows-style path, so
  250          * we have to convert it.
  251          */
  252         if (home) {
  253             unsigned char *new_path;
  254             ssize_t sz = cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, home, NULL, 0);
  255             if (sz < 0)
  256                 goto skip_path_conv;
  257             new_path = mem_alloc(sz);
  258             sz = cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, home, new_path, sz);
  259             if (sz < 0) {
  260                 mem_free(new_path);
  261                 goto skip_path_conv;
  262             }
  263             mem_free(home);
  264             home = new_path;
  265 skip_path_conv:;
  266         }
  267 #endif
  268         if (home && (stat(home, &st) == -1 || !S_ISDIR(st.st_mode))) {
  269             mem_free(home);
  270             home = NULL;
  271         }
  272     }
  273 #endif
  274     if (!home) home = stracpy(getenv("HOME"));
  275 #ifdef WIN32
  276 /* When we run in Cygwin without Cygwin environment, it reports home "/".
  277    Unfortunatelly, it can't write anything to that directory */
  278     if (home && !strcmp(home, "/")) {
  279         mem_free(home);
  280         home = NULL;
  281     }
  282 #endif
  283     if (!home) {
  284         int i;
  285         home = stracpy(path_to_exe);
  286         if (!home) {
  287             if (config_dir) mem_free(config_dir);
  288             return NULL;
  289         }
  290         for (i = strlen(home) - 1; i >= 0; i--) if (dir_sep(home[i])) {
  291             home[i + 1] = 0;
  292             goto br;
  293         }
  294         home[0] = 0;
  295         br:;
  296     }
  297     while (home[0] && dir_sep(home[strlen(home) - 1])) home[strlen(home) - 1] = 0;
  298     if (home[0]) add_to_strn(&home, "/");
  299     home_links = stracpy(home);
  300     if (config_dir)     
  301     {
  302         add_to_strn(&home_links, config_dir);
  303         while (home_links[0] && dir_sep(home_links[strlen(home_links) - 1])) home_links[strlen(home_links) - 1] = 0;
  304         if (stat(home_links, &st) != -1 && S_ISDIR(st.st_mode)) {
  305             add_to_strn(&home_links, "/links");
  306             } else {
  307             fprintf(stderr, "CONFIG_DIR set to %s. But directory %s doesn't exist.\n\007", config_dir, home_links);
  308             sleep(3);
  309             mem_free(home_links);
  310             home_links = stracpy(home);
  311             add_to_strn(&home_links, ".links");     
  312         }
  313         mem_free(config_dir);
  314     } else add_to_strn(&home_links, ".links");
  315     if (stat(home_links, &st)) {
  316 #ifdef HAVE_MKDIR
  317         if (!mkdir(home_links, 0777)) goto home_creat;
  318 #endif
  319         if (config_dir) goto failed;
  320         goto first_failed;
  321     }
  322     if (S_ISDIR(st.st_mode)) goto home_ok;
  323     /* This is a Cygwin hack! Cygwin reports stat for "links" if no
  324        "links" exists and only "links.exe" does. So try to create directory
  325        anyway. */
  326     if (!mkdir(home_links, 0777)) goto home_creat;
  327     first_failed:
  328     mem_free(home_links);
  329     home_links = stracpy(home);
  330     add_to_strn(&home_links, "links");
  331     if (stat(home_links, &st)) {
  332 #ifdef HAVE_MKDIR
  333         if (!mkdir(home_links, 0777)) goto home_creat;
  334 #else
  335         mem_free(home_links);
  336         home_links = stracpy(home);
  337         goto home_ok;
  338 #endif
  339         goto failed;
  340     }
  341     if (S_ISDIR(st.st_mode)) goto home_ok;
  342     if (!mkdir(home_links, 0777)) goto home_creat;
  343     failed:
  344     mem_free(home_links);
  345     mem_free(home);
  346     return NULL;
  347 
  348     home_ok:
  349     if (n) *n = 0;
  350     home_creat:
  351 #ifdef HAVE_CHMOD
  352     chmod(home_links, 0700);
  353 #endif
  354     add_to_strn(&home_links, "/");
  355     mem_free(home);
  356     return home_links;
  357 }
  358 
  359 void init_home()
  360 {
  361     get_system_name();
  362     links_home = get_home(&first_use);
  363     if (!links_home) {
  364         fprintf(stderr, "Unable to find or create links config directory. Please check, that you have $HOME variable set correctly and that you have write permission to your home directory.\n\007");
  365         sleep(3);
  366         return;
  367     }
  368 }
  369 
  370 void load_config_file(unsigned char *prefix, unsigned char *name)
  371 {
  372     unsigned char *c, *config_file;
  373     config_file = stracpy(prefix);
  374     if (!config_file) return;
  375     add_to_strn(&config_file, name);
  376     if ((c = read_config_file(config_file))) goto ok;
  377     mem_free(config_file);
  378     config_file = stracpy(prefix);
  379     if (!config_file) return;
  380     add_to_strn(&config_file, ".");
  381     add_to_strn(&config_file, name);
  382     if ((c = read_config_file(config_file))) goto ok;
  383     mem_free(config_file);
  384     return;
  385     ok:
  386     parse_config_file(config_file, c, all_options);
  387     mem_free(c);
  388     mem_free(config_file);
  389 }
  390 
  391 void load_config()
  392 {
  393 #ifdef SHARED_CONFIG_DIR
  394     load_config_file(SHARED_CONFIG_DIR, "links.cfg");
  395 #endif
  396     load_config_file(links_home, "links.cfg");
  397     load_config_file(links_home, "html.cfg");
  398     load_config_file(links_home, "user.cfg");
  399 }
  400 
  401 int write_config_data(unsigned char *prefix, unsigned char *name, struct option *o, struct terminal *term)
  402 {
  403     int err;
  404     unsigned char *c, *config_file;
  405     if (!(c = create_config_string(o))) return -1;
  406     config_file = stracpy(prefix);
  407     if (!config_file) {
  408         mem_free(c);
  409         return -1;
  410     }
  411     add_to_strn(&config_file, name);
  412     if ((err = write_to_config_file(config_file, c))) {
  413         if (term) msg_box(term, NULL, TEXT_(T_CONFIG_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_UNABLE_TO_WRITE_TO_CONFIG_FILE), ": ", get_err_msg(err), NULL, NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
  414         mem_free(c);
  415         mem_free(config_file);
  416         return -1;
  417     }
  418     mem_free(c);
  419     mem_free(config_file);
  420     return 0;
  421 }
  422 
  423 void write_config(struct terminal *term)
  424 {
  425     write_config_data(links_home, "links.cfg", links_options, term);
  426 }
  427 
  428 void write_html_config(struct terminal *term)
  429 {
  430     write_config_data(links_home, "html.cfg", html_options, term);
  431 }
  432 
  433 void add_nm(struct option *o, unsigned char **s, int *l)
  434 {
  435     if (*l) add_to_str(s, l, NEWLINE);
  436     add_to_str(s, l, o->cfg_name);
  437     add_to_str(s, l, " ");
  438 }
  439 
  440 void add_quoted_to_str(unsigned char **s, int *l, unsigned char *q)
  441 {
  442     add_chr_to_str(s, l, '"');
  443     while (*q) {
  444         if (*q == '"' || *q == '\\') add_chr_to_str(s, l, '\\');
  445         add_chr_to_str(s, l, *q);
  446         q++;
  447     }
  448     add_chr_to_str(s, l, '"');
  449 }
  450 
  451 unsigned char *num_rd(struct option *o, unsigned char *c)
  452 {
  453     unsigned char *tok = get_token(&c);
  454     unsigned char *end;
  455     long l;
  456     if (!tok) return "Missing argument";
  457     l = strtolx(tok, &end);
  458     if (*end) {
  459         mem_free(tok);
  460         return "Number expected";
  461     }
  462     if (l < o->min || l > o->max) {
  463         mem_free(tok);
  464         return "Out of range";
  465     }
  466     *(int *)o->ptr = l;
  467     mem_free(tok);
  468     return NULL;
  469 }
  470 
  471 void num_wr(struct option *o, unsigned char **s, int *l)
  472 {
  473     add_nm(o, s, l);
  474     add_knum_to_str(s, l, *(int *)o->ptr);
  475 }
  476 
  477 unsigned char *str_rd(struct option *o, unsigned char *c)
  478 {
  479     unsigned char *tok = get_token(&c);
  480     unsigned char *e = NULL;
  481     if (!tok) return NULL;
  482     if (strlen(tok) + 1 > (size_t)o->max) e = "String too long";
  483     else strcpy(o->ptr, tok);
  484     mem_free(tok);
  485     return e;
  486 }
  487 
  488 void str_wr(struct option *o, unsigned char **s, int *l)
  489 {
  490     add_nm(o, s, l);
  491     if (strlen(o->ptr) + 1 > (size_t)o->max) {
  492         unsigned char *s1 = init_str();
  493         int l1 = 0;
  494         add_bytes_to_str(&s1, &l1, o->ptr, o->max - 1);
  495         add_quoted_to_str(s, l, s1);
  496         mem_free(s1);
  497     }
  498     else add_quoted_to_str(s, l, o->ptr);
  499 }
  500 
  501 unsigned char *cp_rd(struct option *o, unsigned char *c)
  502 {
  503     unsigned char *tok = get_token(&c);
  504     unsigned char *e = NULL;
  505     int i;
  506     if (!tok) return "Missing argument";
  507     if ((i = get_cp_index(tok)) == -1) e = "Unknown codepage";
  508     else if (o->min == 1 && is_cp_special(i)) e = "UTF-8 can't be here";
  509     else *(int *)o->ptr = i;
  510     mem_free(tok);
  511     return e;
  512 }
  513 
  514 void cp_wr(struct option *o, unsigned char **s, int *l)
  515 {
  516     unsigned char *n = get_cp_mime_name(*(int *)o->ptr);
  517     add_nm(o, s, l);
  518     add_to_str(s, l, n);
  519 }
  520 
  521 unsigned char *lang_rd(struct option *o, unsigned char *c)
  522 {
  523     int i;
  524     unsigned char *tok = get_token(&c);
  525     if (!tok) return "Missing argument";
  526     for (i = 0; i < n_languages(); i++)
  527         if (!(strcasecmp(language_name(i), tok))) {
  528             set_language(i);
  529             mem_free(tok);
  530             return NULL;
  531         }
  532     mem_free(tok);
  533     return "Unknown language";
  534 }
  535 
  536 void lang_wr(struct option *o, unsigned char **s, int *l)
  537 {
  538     add_nm(o, s, l);
  539     add_quoted_to_str(s, l, language_name(current_language));
  540 }
  541 
  542 int getnum(unsigned char *s, int *n, int r1, int r2)
  543 {
  544     unsigned char *e;
  545     long l = strtol(s, (char **)(void *)&e, 10);
  546     if (*e || !*s) return -1;
  547     if (l < r1 || l >= r2) return -1;
  548     *n = (int)l;
  549     return 0;
  550 }
  551 
  552 unsigned char *type_rd(struct option *o, unsigned char *c)
  553 {
  554     unsigned char *err = "Error reading association specification";
  555     struct assoc new;
  556     unsigned char *w;
  557     int n;
  558     memset(&new, 0, sizeof(struct assoc));
  559     if (!(new.label = get_token(&c))) goto err;
  560     if (!(new.ct = get_token(&c))) goto err;
  561     if (!(new.prog = get_token(&c))) goto err;
  562     if (!(w = get_token(&c))) goto err;
  563     if (getnum(w, &n, 0, 32)) goto err_f;
  564     mem_free(w);
  565     new.cons = !!(n & 1);
  566     new.xwin = !!(n & 2);
  567     new.ask = !!(n & 4);
  568     if ((n & 8) || (n & 16)) new.block = !!(n & 16);
  569     else new.block = !new.xwin || new.cons;
  570     if (!(w = get_token(&c))) goto err;
  571     if (strlen(w) != 1 || w[0] < '0' || w[0] > '9') goto err_f;
  572     new.system = w[0] - '0';
  573     mem_free(w);
  574     update_assoc(&new);
  575     err = NULL;
  576     err:
  577     if (new.label) mem_free(new.label);
  578     if (new.ct) mem_free(new.ct);
  579     if (new.prog) mem_free(new.prog);
  580     return err;
  581     err_f:
  582     mem_free(w);
  583     goto err;
  584 }
  585 
  586 void type_wr(struct option *o, unsigned char **s, int *l)
  587 {
  588     struct assoc *a;
  589     foreachback(a, assoc) {
  590         add_nm(o, s, l);
  591         add_quoted_to_str(s, l, a->label);
  592         add_to_str(s, l, " ");
  593         add_quoted_to_str(s, l, a->ct);
  594         add_to_str(s, l, " ");
  595         add_quoted_to_str(s, l, a->prog);
  596         add_to_str(s, l, " ");
  597         add_num_to_str(s, l, (!!a->cons) + (!!a->xwin) * 2 + (!!a->ask) * 4 + (!a->block) * 8 + (!!a->block) * 16);
  598         add_to_str(s, l, " ");
  599         add_num_to_str(s, l, a->system);
  600     }
  601 }
  602 
  603 unsigned char *ext_rd(struct option *o, unsigned char *c)
  604 {
  605     unsigned char *err = "Error reading extension specification";
  606     struct extension new;
  607     memset(&new, 0, sizeof(struct extension));
  608     if (!(new.ext = get_token(&c))) goto err;
  609     if (!(new.ct = get_token(&c))) goto err;
  610     update_ext(&new);
  611     err = NULL;
  612     err:
  613     if (new.ext) mem_free(new.ext);
  614     if (new.ct) mem_free(new.ct);
  615     return err;
  616 }
  617 
  618 void ext_wr(struct option *o, unsigned char **s, int *l)
  619 {
  620     struct extension *a;
  621     foreachback(a, extensions) {
  622         add_nm(o, s, l);
  623         add_quoted_to_str(s, l, a->ext);
  624         add_to_str(s, l, " ");
  625         add_quoted_to_str(s, l, a->ct);
  626     }
  627 }
  628 
  629 unsigned char *prog_rd(struct option *o, unsigned char *c)
  630 {
  631     unsigned char *err = "Error reading program specification";
  632     unsigned char *prog, *w;
  633     if (!(prog = get_token(&c))) goto err_1;
  634     if (!(w = get_token(&c))) goto err_2;
  635     if (strlen(w) != 1 || w[0] < '0' || w[0] > '9') goto err_3;
  636     update_prog(o->ptr, prog, w[0] - '0');
  637     err = NULL;
  638     err_3:
  639     mem_free(w);
  640     err_2:
  641     mem_free(prog);
  642     err_1:
  643     return err;
  644 }
  645 
  646 void prog_wr(struct option *o, unsigned char **s, int *l)
  647 {
  648     struct protocol_program *a;
  649     foreachback(a, *(struct list_head *)o->ptr) {
  650         if (!*a->prog) continue;
  651         add_nm(o, s, l);
  652         add_quoted_to_str(s, l, a->prog);
  653         add_to_str(s, l, " ");
  654         add_num_to_str(s, l, a->system);
  655     }
  656 }
  657 
  658 unsigned char *term_rd(struct option *o, unsigned char *c)
  659 {
  660     struct term_spec *ts;
  661     unsigned char *w;
  662     int i;
  663     if (!(w = get_token(&c))) goto err;
  664     if (!(ts = new_term_spec(w))) {
  665         mem_free(w);
  666         goto end;
  667     }
  668     mem_free(w);
  669     if (!(w = get_token(&c))) goto err;
  670     if (strlen(w) != 1 || w[0] < '0' || w[0] > '4') goto err_f;
  671     ts->mode = w[0] - '0';
  672     mem_free(w);
  673     if (!(w = get_token(&c))) goto err;
  674     if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
  675     ts->m11_hack = w[0] - '0';
  676     mem_free(w);
  677     if (!(w = get_token(&c))) goto err;
  678     if (strlen(w) != 1 || w[0] < '0' || w[0] > '7') goto err_f;
  679     ts->col = (w[0] - '0') & 1;
  680     ts->restrict_852 = !!((w[0] - '0') & 2);
  681     ts->block_cursor = !!((w[0] - '0') & 4);
  682     mem_free(w);
  683     if (!(w = get_token(&c))) goto err;
  684     if ((i = get_cp_index(w)) == -1 || is_cp_special(i)) goto err_f;
  685     ts->charset = i;
  686     mem_free(w);
  687     end:
  688     return NULL;
  689     err_f:
  690     mem_free(w);
  691     err:
  692     return "Error reading terminal specification";
  693 }
  694 
  695 unsigned char *term2_rd(struct option *o, unsigned char *c)
  696 {
  697     struct term_spec *ts;
  698     unsigned char *w;
  699     int i;
  700     if (!(w = get_token(&c))) goto err;
  701     if (!(ts = new_term_spec(w))) {
  702         mem_free(w);
  703         goto end;
  704     }
  705     mem_free(w);
  706     if (!(w = get_token(&c))) goto err;
  707     if (strlen(w) != 1 || w[0] < '0' || w[0] > '4') goto err_f;
  708     ts->mode = w[0] - '0';
  709     mem_free(w);
  710     if (!(w = get_token(&c))) goto err;
  711     if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
  712     ts->m11_hack = w[0] - '0';
  713     mem_free(w);
  714     if (!(w = get_token(&c))) goto err;
  715     if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
  716     ts->restrict_852 = w[0] - '0';
  717     mem_free(w);
  718     if (!(w = get_token(&c))) goto err;
  719     if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
  720     ts->col = w[0] - '0';
  721     mem_free(w);
  722     if (!(w = get_token(&c))) goto err;
  723     if ((i = get_cp_index(w)) == -1 || is_cp_special(i)) goto err_f;
  724     ts->charset = i;
  725     mem_free(w);
  726     end:
  727     return NULL;
  728     err_f:
  729     mem_free(w);
  730     err:
  731     return "Error reading terminal specification";
  732 }
  733 
  734 void term_wr(struct option *o, unsigned char **s, int *l)
  735 {
  736     struct term_spec *ts;
  737     foreachback(ts, term_specs) {
  738         add_nm(o, s, l);
  739         add_quoted_to_str(s, l, ts->term);
  740         add_to_str(s, l, " ");
  741         add_num_to_str(s, l, ts->mode);
  742         add_to_str(s, l, " ");
  743         add_num_to_str(s, l, ts->m11_hack);
  744         add_to_str(s, l, " ");
  745         add_num_to_str(s, l, !!ts->col + !!ts->restrict_852 * 2 + !!ts->block_cursor * 4);
  746         add_to_str(s, l, " ");
  747         add_to_str(s, l, get_cp_mime_name(ts->charset));
  748     }
  749 }
  750 
  751 unsigned char *gen_cmd(struct option *o, unsigned char ***argv, int *argc)
  752 {
  753     unsigned char *e;
  754     int l;
  755     unsigned char *r;
  756     if (!*argc) return "Parameter expected";
  757     e = init_str();
  758     l = 0;
  759     add_quoted_to_str(&e, &l, **argv);
  760     r = o->rd_cfg(o, e);
  761     mem_free(e);
  762     if (r) return r;
  763     (*argv)++; (*argc)--;
  764     return NULL;
  765 }
  766 
  767 unsigned char *lookup_cmd(struct option *o, unsigned char ***argv, int *argc)
  768 {
  769     ip addr;
  770     unsigned char *p = (unsigned char *)&addr;
  771     if (!*argc) return "Parameter expected";
  772     if (*argc >= 2) return "Too many parameters";
  773     (*argv)++; (*argc)--;
  774     if (do_real_lookup(*(*argv - 1), &addr)) {
  775 #if defined(HAVE_GETHOSTBYNAME) && defined(HAVE_HERROR)
  776         herror("error");
  777 #else
  778         fprintf(stderr, "error: host not found\n");
  779 #endif
  780         return "";
  781     }
  782     printf("%d.%d.%d.%d\n", (int)p[0], (int)p[1], (int)p[2], (int)p[3]);
  783     fflush(stdout);
  784     return "";
  785 }
  786 
  787 unsigned char *version_cmd(struct option *o, unsigned char ***argv, int *argc)
  788 {
  789     printf("Links " VERSION_STRING "\n");
  790     fflush(stdout);
  791     return "";
  792 }
  793 
  794 unsigned char *no_connect_cmd(struct option *o, unsigned char ***argv, int *argc)
  795 {
  796     no_connect = 1;
  797     return NULL;
  798 }
  799 
  800 unsigned char *anonymous_cmd(struct option *o, unsigned char ***argv, int *argc)
  801 {
  802     anonymous = 1;
  803     return NULL;
  804 }
  805 
  806 unsigned char *force_html_cmd(struct option *o, unsigned char ***argv, int *argc)
  807 {
  808     force_html = 1;
  809     return NULL;
  810 }
  811 
  812 unsigned char *dump_cmd(struct option *o, unsigned char ***argv, int *argc)
  813 {
  814     if (dmp != o->min && dmp) return "Can't use both -dump and -source";
  815     dmp = o->min;
  816     no_connect = 1;
  817     return NULL;
  818 }
  819 
  820 unsigned char *printhelp_cmd(struct option *o, unsigned char ***argv, int *argc)
  821 {
  822 /* Changed and splited - translation is much easier.
  823  * Print to stdout instead stderr (,,links -help | more''
  824  * is much better than ,,links -help 2>&1 | more'').
  825  */
  826 fprintf(stdout, "%s%s%s%s%s%s\n", 
  827 
  828 ("links [options] URL\n\
  829 Options are:\n\
  830 \n\
  831  -async-dns <0>/<1>\n\
  832   Asynchronous DNS resolver on(1)/off(0).\n\
  833 \n\
  834  -download-utime <0>/<1>\n\
  835   Set time of downloaded files to time from server.\n\
  836 \n\
  837  -max-connections <max>\n\
  838   Maximum number of concurrent connections.\n\
  839   (default: 10)\n\
  840 \n"),
  841 (" -max-connections-to-host <max>\n\
  842   Maximum number of concurrent connection to a given host.\n\
  843   (default: 2)\n\
  844 \n\
  845  -retries <retry>\n\
  846   Number of retries.\n\
  847   (default: 3)\n\
  848 \n\
  849  -receive-timeout <sec>\n\
  850   Timeout on receive.\n\
  851   (default: 120)\n\
  852 \n"),
  853 (" -unrestartable-receive-timeout <sec>\n\
  854   Timeout on non restartable connections.\n\
  855   (default: 600)\n\
  856 \n\
  857  -format-cache-size <num>\n\
  858   Number of formatted document pages cached.\n\
  859   (default: 5)\n\
  860 \n\
  861  -memory-cache-size <Kbytes>\n\
  862   Cache memory in Kilobytes.\n\
  863   (default: 1024)\n\
  864 \n"),
  865 (" -http-proxy <host:port>\n\
  866   Host and port number of the HTTP proxy, or blank.\n\
  867   (default: blank)\n\
  868 \n\
  869  -ftp-proxy <host:port>\n\
  870   Host and port number of the FTP proxy, or blank.\n\
  871   (default: blank)\n\
  872 \n\
  873  -download-dir <path>\n\
  874   Default download directory.\n\
  875   (default: actual dir)\n\
  876 \n\
  877  -http-bugs.http10 <0>/<1>\n\
  878   \"1\" forces using only HTTP/1.0 protocol.\n\
  879 \n\
  880  -http-bugs.allow-blacklist <0>/<1>\n\
  881   \"1\" defaults to using list of servers that have broken HTTP/1.1 support.\n\
  882   When links finds such server, it will retry the request with HTTP/1.0.\n\
  883 \n\
  884  -http-bugs.bug-302-redirect <0>/<1>\n\
  885   Process 302 redirect in a way that is incompatible with RFC1945 and RFC2068,\n\
  886   but the same as Netscape and MSIE. Many pages depend on it.\n\
  887 \n\
  888  -http-bugs.bug-post-no-keepalive <0>/<1>\n\
  889   No keepalive connection after post requests. For some buggy servers.\n\
  890 \n\
  891  -http-bugs.bug-no-accept-charset <0>/<1>\n\
  892   Do not send Accept-Charset field of HTTP header.\n\
  893 \n\
  894  -ftp.anonymous-password <string>\n\
  895   Use ftp PASV command to bypass firewalls.\n\
  896 \n\
  897  -ftp.fast <0>/<1>\n\
  898   Send more ftp commands simultaneously. Faster response when\n\
  899   browsing ftp directories, but it is incompatible with RFC\n\
  900   and some servers don't like it.\n\
  901 \n\
  902  -ftp.set-iptos <0>/<1>\n\
  903   Set IP Type-of-service to high throughput on ftp connections.\n\
  904 \n"),
  905 (" -html-assume-codepage <codepage>\n\
  906   Use the given codepage when the webpage did not specify\n\
  907   its codepage. (default: ISO 8859-1)\n\
  908 \n\
  909  -html-tables <0>/<1>\n\
  910   Render tables.\n\
  911 \n\
  912  -html-frames <0>/<1>\n\
  913   Render frames.\n\
  914 \n\
  915  -html-images <0>/<1>\n\
  916   Display links to images.\n\
  917 \n\
  918  -html-numbered-links <0>/<1>\n\
  919   Link numbering.\n\
  920 \n\
  921  -html-table-order <0>/<1>\n\
  922   Walk table by rows (0) or columns (1).\n\
  923 \n\
  924  -html-margin <margin>\n\
  925   Text margin.\n\
  926 \n\
  927  -language <language>\n\
  928   User interface language.\n\
  929 \n\
  930  -anonymous\n\
  931   Restrict links so that it can run on an anonymous account.\n\
  932   No local file browsing. No downloads. Executing of viewers\n\
  933   is allowed, but user can't add or modify entries in\n\
  934   association table.\n\
  935 \n\
  936  -force-html\n\
  937   Treat file as if it had an .html extension.\n\
  938 \n\
  939  -source\n\
  940   Write the given HTML document in source form to stdout.\n\
  941 \n\
  942  -dump\n\
  943   Write a plain-text version of the given HTML document to\n\
  944   stdout.\n\
  945 \n\
  946  -width <size>\n\
  947   Size of screen in characters, used in combination with -dump.\n\
  948 \n\
  949  -codepage <codepage>\n\
  950   Character set of output of -dump.\n\
  951 \n\
  952  -no-connect\n\
  953   Runs links as a separate instance - instead of connecting to\n\
  954   existing instance.\n\
  955 \n\
  956  -lookup <host>\n\
  957   Do lookup like \"host\" command.\n\
  958 \n\
  959  -version\n\
  960   Prints the links version number and exit.\n\
  961 \n\
  962  -help\n\
  963   Prints this help screen\n\
  964 \n\
  965 \n"),
  966 ("Keys:\n\
  967     ESC  display menu\n\
  968     ^C   quit\n\
  969     ^P, ^N   scroll up, down\n\
  970     [, ]     scroll left, right\n\
  971     up, down select link\n\
  972     ->   follow link\n\
  973     <-   go back\n\
  974     g    go to url\n\
  975     G    go to url based on current url\n\
  976     ^R   reload\n\
  977     /    search\n\
  978     ?    search back\n\
  979     n    find next\n\
  980     N    find previous\n\
  981     =    document info\n\
  982     \\   document source\n\
  983     |    HTTP header\n\
  984     *    toggle displaying of image links\n\
  985     d    download\n\
  986     s    bookmarks\n\
  987     q    quit\n"));
  988 
  989     fflush(stdout);
  990     return "";
  991 }
  992 
  993 void end_config()
  994 {
  995     if (links_home) mem_free(links_home);
  996 }
  997 
  998 int anonymous = 0;
  999 
 1000 unsigned char system_name[MAX_STR_LEN];
 1001 
 1002 unsigned char *links_home = NULL;
 1003 int first_use = 0;
 1004 int created_home = 0;
 1005 
 1006 int no_connect = 0;
 1007 int base_session = 0;
 1008 int dmp = 0;
 1009 int force_html = 0;
 1010 
 1011 int async_lookup = 1;
 1012 int download_utime = 0;
 1013 int max_connections = 10;
 1014 int max_connections_to_host = 2;
 1015 int max_tries = 3;
 1016 int receive_timeout = 120;
 1017 int unrestartable_receive_timeout = 600;
 1018 
 1019 int screen_width = 80;
 1020 int dump_codepage = -1;
 1021 
 1022 int max_format_cache_entries = 5;
 1023 int memory_cache_size = 1048576;
 1024 
 1025 int enable_html_tables = 1;
 1026 int enable_html_frames = 1;
 1027 int display_images = 1;
 1028 
 1029 struct document_setup dds = { 0, 0, 1, 1, 0, 3, 0, 0 };
 1030 
 1031 struct rgb default_fg = { 191, 191, 191, 0 };
 1032 struct rgb default_bg = { 0, 0, 0, 0 };
 1033 struct rgb default_link = { 255, 255, 255, 0 };
 1034 struct rgb default_vlink = { 255, 255, 0, 0 };
 1035 
 1036 int default_left_margin = HTML_LEFT_MARGIN;
 1037 
 1038 unsigned char http_proxy[MAX_STR_LEN] = "";
 1039 unsigned char ftp_proxy[MAX_STR_LEN] = "";
 1040 
 1041 unsigned char download_dir[MAX_STR_LEN] = "";
 1042 
 1043 struct ftp_options ftp_options = { "somebody@host.domain", 0, 0, 1 };
 1044 
 1045 /* These are workarounds for some CGI script bugs */
 1046 struct http_bugs http_bugs = { 0, 1, 1, 0, 0 };
 1047 /*int bug_302_redirect = 0;*/
 1048     /* When got 301 or 302 from POST request, change it to GET
 1049        - this violates RFC2068, but some buggy message board scripts rely on it */
 1050 /*int bug_post_no_keepalive = 0;*/
 1051     /* No keepalive connection after POST request. Some buggy PHP databases report bad
 1052        results if GET wants to retreive data POSTed in the same connection */
 1053 
 1054 struct option links_options[] = {
 1055     { 1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "?" },
 1056     { 1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "h" },
 1057     { 1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "help" },
 1058     { 1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "-help" },
 1059     { 1, lookup_cmd, NULL, NULL, 0, 0, NULL, NULL, "lookup" },
 1060     { 1, version_cmd, NULL, NULL, 0, 0, NULL, NULL, "version" },
 1061     { 1, no_connect_cmd, NULL, NULL, 0, 0, NULL, NULL, "no-connect" },
 1062     { 1, anonymous_cmd, NULL, NULL, 0, 0, NULL, NULL, "anonymous" },
 1063     { 1, gen_cmd, num_rd, NULL, 0, MAXINT, &base_session, NULL, "base-session" },
 1064     { 1, force_html_cmd, NULL, NULL, 0, 0, NULL, NULL, "force-html" },
 1065     { 1, dump_cmd, NULL, NULL, D_SOURCE, 0, NULL, NULL, "source" },
 1066     { 1, dump_cmd, NULL, NULL, D_DUMP, 0, NULL, NULL, "dump" },
 1067     { 1, gen_cmd, num_rd, NULL, 10, 512, &screen_width, "dump_width", "width" },
 1068     { 1, gen_cmd, cp_rd, NULL, 1, 0, &dump_codepage, "dump_codepage", "codepage" },
 1069     { 1, gen_cmd, num_rd, num_wr, 0, 1, &async_lookup, "async_dns", "async-dns" },
 1070     { 1, gen_cmd, num_rd, num_wr, 0, 1, &download_utime, "download_utime", "download-utime" },
 1071     { 1, gen_cmd, num_rd, num_wr, 1, 99, &max_connections, "max_connections", "max-connections" },
 1072     { 1, gen_cmd, num_rd, num_wr, 1, 99, &max_connections_to_host, "max_connections_to_host", "max-connections-to-host" },
 1073     { 1, gen_cmd, num_rd, num_wr, 0, 16, &max_tries, "retries", "retries" },
 1074     { 1, gen_cmd, num_rd, num_wr, 1, 1800, &receive_timeout, "receive_timeout", "receive-timeout" },
 1075     { 1, gen_cmd, num_rd, num_wr, 1, 1800, &unrestartable_receive_timeout, "unrestartable_receive_timeout", "unrestartable-receive-timeout" },
 1076     { 1, gen_cmd, num_rd, num_wr, 0, 256, &max_format_cache_entries, "format_cache_size", "format-cache-size" },
 1077     { 1, gen_cmd, num_rd, num_wr, 0, MAXINT, &memory_cache_size, "memory_cache_size", "memory-cache-size" },
 1078     { 1, gen_cmd, str_rd, str_wr, 0, MAX_STR_LEN, http_proxy, "http_proxy", "http-proxy" },
 1079     { 1, gen_cmd, str_rd, str_wr, 0, MAX_STR_LEN, ftp_proxy, "ftp_proxy", "ftp-proxy" },
 1080     { 1, gen_cmd, str_rd, str_wr, 0, MAX_STR_LEN, download_dir, "download_dir", "download-dir" },
 1081     { 1, gen_cmd, lang_rd, lang_wr, 0, 0, &current_language, "language", "language" },
 1082     { 1, gen_cmd, num_rd, num_wr, 0, 1, &http_bugs.http10, "http_bugs.http10", "http-bugs.http10" },
 1083     { 1, gen_cmd, num_rd, num_wr, 0, 1, &http_bugs.allow_blacklist, "http_bugs.allow_blacklist", "http-bugs.allow-blacklist" },
 1084     { 1, gen_cmd, num_rd, num_wr, 0, 1, &http_bugs.bug_302_redirect, "http_bugs.bug_302_redirect", "http-bugs.bug-302-redirect" },
 1085     { 1, gen_cmd, num_rd, num_wr, 0, 1, &http_bugs.bug_post_no_keepalive, "http_bugs.bug_post_no_keepalive", "http-bugs.bug-post-no-keepalive" },
 1086     { 1, gen_cmd, num_rd, num_wr, 0, 1, &http_bugs.no_accept_charset, "http_bugs.no_accept_charset", "http-bugs.bug-no-accept-charset" },
 1087     { 1, gen_cmd, str_rd, str_wr, 0, MAX_STR_LEN, ftp_options.anon_pass, "ftp.anonymous_password", "ftp.anonymous-password" },
 1088     { 1, gen_cmd, num_rd, num_wr, 0, 1, &ftp_options.passive_ftp, "ftp.use_passive", "ftp.use-passive" },
 1089     { 1, gen_cmd, num_rd, num_wr, 0, 1, &ftp_options.fast_ftp, "ftp.fast", "ftp.fast" },
 1090     { 1, gen_cmd, num_rd, num_wr, 0, 1, &ftp_options.set_tos, "ftp.set_iptos", "ftp.set-iptos" },
 1091     { 1, gen_cmd, cp_rd, NULL, 0, 0, &dds.assume_cp, "assume_codepage", "assume-codepage" },
 1092     { 1, NULL, term_rd, term_wr, 0, 0, NULL, "terminal", NULL },
 1093     { 1, NULL, term2_rd, NULL, 0, 0, NULL, "terminal2", NULL },
 1094     { 1, NULL, type_rd, type_wr, 0, 0, NULL, "association", NULL },
 1095     { 1, NULL, ext_rd, ext_wr, 0, 0, NULL, "extension", NULL },
 1096     { 1, NULL, prog_rd, prog_wr, 0, 0, &mailto_prog, "mailto", NULL },
 1097     { 1, NULL, prog_rd, prog_wr, 0, 0, &telnet_prog, "telnet", NULL },
 1098     { 1, NULL, prog_rd, prog_wr, 0, 0, &tn3270_prog, "tn3270", NULL },
 1099     { 1, NULL, prog_rd, prog_wr, 0, 0, &mms_prog, "mms", NULL },
 1100     { 1, NULL, bind_rd, NULL, 0, 0, NULL, "bind", NULL },
 1101     { 1, NULL, unbind_rd, NULL, 0, 0, NULL, "unbind", NULL },
 1102     { 0, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL },
 1103 };
 1104 
 1105 struct option html_options[] = {
 1106     { 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.hard_assume, "html_hard_assume", "html-hard-assume" },
 1107     { 1, gen_cmd, cp_rd, cp_wr, 0, 0, &dds.assume_cp, "html_assume_codepage", "html-assume-codepage" },
 1108     { 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.tables, "html_tables", "html-tables" },
 1109     { 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.frames, "html_frames", "html-frames" },
 1110     { 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.images, "html_images", "html-images" },
 1111     { 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.num_links, "html_numbered_links", "html-numbered-links" },
 1112     { 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.table_order, "html_table_order", "html-table-order" },
 1113     { 1, gen_cmd, num_rd, num_wr, 0, 9, &dds.margin, "html_margin", "html-margin" },
 1114     { 0, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL },
 1115 };
 1116 
 1117 void load_url_history(void)
 1118 {
 1119     unsigned char *history_file, *hs;
 1120     unsigned char *hsp;
 1121 
 1122     if (anonymous) return;
 1123     /* Must have been called after init_home */
 1124     if (!links_home) return;
 1125     history_file = stracpy(links_home);
 1126     add_to_strn(&history_file, "links.his");
 1127     hs = read_config_file(history_file);
 1128     mem_free(history_file);
 1129     if (!hs) return;
 1130     for (hsp = hs; *hsp; ) {
 1131         unsigned char *hsl, *hsc;
 1132         for (hsl = hsp; *hsl && *hsl != 10 && *hsl != 13; hsl++) ;
 1133         hsc = memacpy(hsp, hsl - hsp);
 1134         add_to_history(&goto_url_history, hsc, 0);
 1135         mem_free(hsc);
 1136         hsp = hsl;
 1137         while (*hsp == 10 || *hsp == 13) hsp++;
 1138     }
 1139     mem_free(hs);
 1140 }
 1141 
 1142 void save_url_history(void)
 1143 {
 1144     struct history_item *hi;
 1145     unsigned char *history_file;
 1146     unsigned char *hs;
 1147     int hsl = 0;
 1148     int i = 0;
 1149     if (anonymous) return;
 1150 
 1151     /* Must have been called after init_home */
 1152     if (!links_home) return;
 1153     history_file = stracpy(links_home);
 1154     add_to_strn(&history_file, "links.his");
 1155     hs = init_str();
 1156     hsl = 0;
 1157     foreachback(hi, goto_url_history.items) {
 1158         if (!*hi->d || strchr(hi->d, 10) || strchr(hi->d, 13)) continue;
 1159         if (i++ > MAX_HISTORY_ITEMS)
 1160             break;
 1161         else {
 1162             add_to_str(&hs, &hsl, hi->d);
 1163             add_to_str(&hs, &hsl, NEWLINE);
 1164         }
 1165     }
 1166     write_to_config_file(history_file, hs);
 1167     mem_free(history_file);
 1168     mem_free(hs);
 1169     return;
 1170 }
 1171