home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / nn.tar / nn-6.5.1 / folder.c < prev    next >
C/C++ Source or Header  |  1995-04-29  |  12KB  |  578 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Folder handling
  5.  */
  6.  
  7. /*#include <errno.h>*/
  8. #include "config.h"
  9. #include "articles.h"
  10. #include "news.h"
  11. #include "keymap.h"
  12. #include "nn_term.h"
  13. #include "menu.h"
  14. #include <pwd.h>
  15.  
  16. /* folder.c */
  17.  
  18. static char *tilde_expansion __APROTO((char **srcp, int compl));
  19. static folder_header __APROTO((void));
  20.  
  21.  
  22. import char *home_directory;
  23. import int  current_folder_type;
  24. import int  use_mail_folders;
  25. import int  use_mmdf_folders;
  26.  
  27. extern int bypass_consolidation;
  28.  
  29. export int  dont_sort_folders = 0;
  30. export char *folder_directory  = NULL;
  31. export int  folder_rewrite_trace = 1;
  32. export char *backup_folder_path = "BackupFolder~";
  33. export int  keep_backup_folder = 1;
  34. export int  convert_folder_mode = 0; /* ignore folder's format on rewrite */
  35. export int  consolidated_manual = 0;
  36. export int  ignore_fancy_select = 0; /* turn off select features for folders */
  37.  
  38. import int fmt_linenum;
  39. import char *header_lines;
  40.  
  41. extern struct passwd *getpwnam();
  42.  
  43. /*
  44.  *    expand ~[user][/...] form
  45.  *    src ptr is advanced to last char of user name.
  46.  */
  47.  
  48. static char *tilde_expansion(srcp, compl)
  49. char **srcp;
  50. int compl;
  51. {
  52.     struct passwd *pwd;
  53.     register char *name = *srcp;
  54.     register char *tail, x;
  55.  
  56.     tail = ++name; /* skip ~ */
  57.     while (*tail && isascii(*tail) && !isspace(*tail) && *tail != '/')
  58.     tail++;
  59.  
  60.     if (compl && *tail != '/') return NULL;
  61.     if (tail == name) return home_directory;
  62.  
  63.     *srcp = tail - 1;
  64.     x = *tail;
  65.     *tail = NUL;
  66.     pwd = getpwnam(name);
  67.     if (pwd == NULL && !compl)
  68.     msg("User %s not found", name);
  69.     *tail = x;
  70.  
  71.     return (pwd == NULL) ? NULL : pwd->pw_dir;
  72. }
  73.  
  74. /*
  75.  *     file name completion and expansion
  76.  *
  77.  *    expand_mode bits:
  78.  *        1:    expand path names
  79.  *        2:    don't expand $N
  80.  *        4:    don't expand any $?  (but $(...) is expanded)
  81.  *        8:    don't complain about ~... (shell will do that)
  82.  *        10:    doing filename completion
  83.  */
  84.  
  85.  
  86. int
  87. expand_file_name(dest, src, expand_mode)
  88. char *dest, *src;
  89. int expand_mode;
  90. {
  91.     register char *cp, *dp, c;
  92.     int parse, remap;
  93.     char *cur_grp, *cur_art;
  94.  
  95.     cur_grp = current_group ? current_group->group_name : NULL;
  96.     cur_art = (group_file_name && *group_file_name) ? group_path_name : NULL;
  97.  
  98.     for (dp = dest, parse = 1; (c = *src); src++) {
  99.  
  100.     if (parse) {
  101.  
  102.         if ((expand_mode & 1) && c == '+') {
  103.         if (folder_directory == NULL) {
  104.             if (!(cp = getenv("FOLDER")))
  105.             cp = FOLDER_DIRECTORY;
  106.             folder_directory = home_relative(cp);
  107.         }
  108.  
  109.         cp = folder_directory;
  110.         goto cp_str;
  111.         }
  112.  
  113.         if ((expand_mode & 1) && c == '~') {
  114.         if ((cp = tilde_expansion(&src, (expand_mode & 0x10))) == NULL) {
  115.             return 0;
  116.         }
  117.  
  118.          cp_str:
  119.         while (*cp) *dp++ = *cp++;
  120.         if (dp[-1] != '/') *dp++ = '/';
  121.         goto no_parse;
  122.         }
  123.  
  124.         if ((expand_mode & 4) == 0 &&
  125.         cur_art && c == '%' && (src[1] == ' ' || src[1] == NUL)) {
  126.         cp = cur_art;
  127.         while (*cp) *dp++ = *cp++;
  128.         goto no_parse;
  129.         }
  130.  
  131.     }
  132.  
  133.     if (c == '$' && src[1] == '(') {
  134.         char envar[64];
  135.         for (src += 2, cp = envar; (c = *src) != NUL && c != ')'; src++)
  136.         *cp++ = c;
  137.         *cp = NUL;
  138.         if (cp != envar) {
  139.         if ((cp = getenv(envar)) != NULL)
  140.             while (*cp) *dp++ = *cp++;
  141.         else {
  142.             msg("Environment variable $(%s) not set", envar);
  143.             return 0;
  144.         }
  145.         }
  146.         goto no_parse;
  147.     }
  148.  
  149.     if ((expand_mode & 4) == 0 && c == '$' && !isalnum(src[2])) {
  150.         remap = 0;
  151.         cp = NULL;
  152.  
  153.         switch (src[1]) {
  154.          case 'A':
  155.         cp = cur_art;
  156.         break;
  157.          case 'F':
  158.         cp = cur_grp;
  159.         remap = 1;
  160.         break;
  161.          case 'G':
  162.         cp = cur_grp;
  163.         break;
  164.          case 'L':
  165.         if ((cp = strrchr(cur_grp, '.')))
  166.             cp++;
  167.         else
  168.             cp = cur_grp;
  169.         break;
  170.          case 'N':
  171.         if (expand_mode & 2) goto copy;
  172.         if (cur_art) cp = group_file_name;
  173.         if (cp == NULL) goto copy;
  174.         break;
  175.          default:
  176.         goto copy;
  177.         }
  178.         src++;
  179.  
  180.         if (!cp) {
  181.         msg("$%c not defined on this level", c);
  182.         return 0;
  183.         }
  184.  
  185.         while (*cp)
  186.         if (remap && *cp == '.')
  187.             cp++, *dp++ = '/';
  188.         else
  189.             *dp++ = *cp++;
  190.         goto no_parse;
  191.     }
  192.  
  193.     if (c == '/')
  194. #ifdef ALLOW_LEADING_DOUBLE_SLASH
  195.       if (dp != &dest[1])
  196. #endif
  197.         if (dp != dest && dp[-1] == '/') goto no_parse;
  198.  
  199.      copy:
  200.     *dp++ = c;
  201.     parse = isspace(c);
  202.     continue;
  203.  
  204.      no_parse:
  205.         parse = 0;
  206.     }
  207.  
  208.     *dp = NUL;
  209.  
  210.     return 1;
  211. }
  212.  
  213.  
  214. int
  215. file_completion(path, index)
  216. char *path;
  217. int index;
  218. {
  219.     static dir_in_use = 0;
  220.     static char *head, *tail = NULL;
  221.     static int  tail_offset;
  222.  
  223.     char nbuf[FILENAME], buffer[FILENAME];
  224.     char *dir, *base;
  225.  
  226.     if (path) {
  227.     if (dir_in_use) {
  228.         close_directory();
  229.         dir_in_use = 0;
  230.     }
  231.  
  232.     if (index < 0) return 0;
  233.  
  234.     head = path;
  235.     tail = path + index;
  236.     }
  237.  
  238.     if (!dir_in_use) {
  239.     path = head;
  240.     *tail = NUL;
  241.  
  242.     if (*path == '|') return -1;    /* no completion for pipes */
  243.  
  244.     if (*path == '+' || *path == '~') {
  245.         if (!expand_file_name(nbuf, path, 0x11))
  246.         return 0;    /* no completions */
  247.     } else
  248.         strcpy(nbuf, path);
  249.  
  250.     if ((base = strrchr(nbuf, '/'))) {
  251.         if (base == nbuf) {
  252.         dir = "/";
  253.         base++;
  254.         } else {
  255.         *base++ = NUL;
  256.         dir = nbuf;
  257.         }
  258.     } else {
  259.         base = nbuf;
  260.         dir = ".";
  261.     }
  262.  
  263.     tail_offset = strlen(base);
  264.  
  265.     dir_in_use = list_directory(dir, base);
  266.  
  267.     return dir_in_use;
  268.     }
  269.  
  270.     if (index)
  271.     return compl_help_directory();
  272.  
  273.     if (!next_directory(buffer, 1)) return 0;
  274.  
  275.     strcpy(tail, buffer+tail_offset);
  276.  
  277.     return 1;
  278. }
  279.  
  280.  
  281. static int cancel_count;
  282.  
  283. void
  284. fcancel(ah)
  285. article_header *ah;
  286. {
  287.     if (ah->attr == A_CANCEL) {
  288.     cancel_count--;
  289.     ah->attr = 0;
  290.     } else {
  291.     cancel_count++;
  292.     ah->attr = A_CANCEL;
  293.     }
  294. }
  295.  
  296. static int
  297. folder_header()
  298. {
  299.     so_printxy(0, 0, "Folder: %s", current_group->group_name);
  300.  
  301.     return 1;    /* number of header lines */
  302. }
  303.  
  304. /*
  305.  *    mode values:
  306.  *        0: normal folder or digest
  307.  *        1: online manual
  308.  *        2: mailbox
  309.  */
  310.  
  311. int
  312. folder_menu(path, mode)
  313. char *path;
  314. int mode;
  315. {
  316.     FILE             *folder;
  317.     register article_header    *ah;
  318.     news_header_buffer         dgbuf;
  319.     char             buffer[256];
  320.     int                more, length, re, menu_cmd, was_raw;
  321.     memory_marker        mem_marker;
  322.     group_header         fake_group;
  323.     int                cc_save;
  324.     char folder_name[FILENAME], folder_file[FILENAME];
  325.     int orig_layout;
  326.     char *orig_hdr_lines;
  327.  
  328.     orig_layout = fmt_linenum;
  329.     orig_hdr_lines = header_lines;
  330.     
  331.     strcpy(folder_name, path);
  332.     fake_group.group_name = folder_name;
  333.     if (!expand_file_name(folder_file, folder_name, 1)) return ME_NO_REDRAW;
  334.     fake_group.archive_file = path = folder_file;
  335.     fake_group.next_group = fake_group.prev_group = NULL;
  336.     fake_group.group_flag = G_FOLDER | G_FAKED;
  337.     fake_group.master_flag = 0;
  338.     fake_group.save_file = NULL;
  339.     current_group = NULL;
  340.     init_group(&fake_group);
  341.  
  342.     folder = open_file(path, OPEN_READ);
  343.     if (folder == NULL) {
  344.     msg("%s not found", path);
  345.     return ME_NO_REDRAW;
  346.     }
  347.  
  348.     switch (get_folder_type(folder)) {
  349.      case 0:
  350.     msg("Reading: %-.65s", path);    break;
  351.      case 1:
  352.      case 2:
  353.     msg("Reading %s folder: %-.50s", 
  354.         current_folder_type==1 ? "mail" : "mmdf", path);
  355.     break;
  356.      default:
  357.     msg("Folder is empty");
  358.     fclose(folder);
  359.     return ME_NO_REDRAW;
  360.     }    
  361.     rewind(folder);
  362.  
  363.     was_raw = no_raw();
  364.     s_keyboard = 0;
  365.  
  366.     current_group = &fake_group;
  367.  
  368.     mark_memory(&mem_marker);
  369.  
  370.     ah = alloc_art();
  371.  
  372.     more = 1;
  373.     while (more && (more = get_digest_article(folder, dgbuf)) >= 0) {
  374.     if (s_keyboard) break;
  375.  
  376.     ah->a_number = 0;
  377.     ah->flag = A_FOLDER;
  378.     ah->attr = 0;
  379.  
  380.     ah->lines = digest.dg_lines;
  381.  
  382.     ah->hpos = digest.dg_hpos;
  383.     ah->fpos = digest.dg_fpos;
  384.     ah->lpos = digest.dg_lpos;
  385.  
  386.     if (digest.dg_from) {
  387.         length = pack_name(buffer, digest.dg_from, NAME_LENGTH);
  388.         ah->sender = alloc_str(length);
  389.         strcpy(ah->sender, buffer);
  390.         ah->name_length = length;
  391.         if (mode == 1) fold_string(ah->sender);
  392.     } else {
  393.         ah->sender = "";
  394.         ah->name_length = 0;
  395.     }
  396.  
  397.     if (digest.dg_subj) {
  398.         length = pack_subject(buffer, digest.dg_subj, &re, 255);
  399.         ah->replies = re;
  400.         ah->subject = alloc_str(length);
  401.         strcpy(ah->subject, buffer);
  402.         ah->subj_length = length;
  403.         if (mode == 1 && length > 1) fold_string(ah->subject + 1);
  404.     } else {
  405.         ah->replies = 0;
  406.         ah->subject = "";
  407.         ah->subj_length = 0;
  408.     }
  409.  
  410.     ah->t_stamp = digest.dg_date ? pack_date(digest.dg_date) : 0;
  411.  
  412.     add_article(ah);
  413.     ah = alloc_art();
  414.     }
  415.  
  416.     fclose(folder);
  417.  
  418.     if (s_keyboard) {
  419.     menu_cmd = ME_NO_REDRAW;
  420.     } else
  421.     if (n_articles == 0) {
  422.     msg("Not a folder (no article header)");
  423.     menu_cmd = ME_NO_REDRAW;
  424.     } else {
  425.     if (n_articles > 1) {
  426.         clrdisp();
  427.         prompt_line = 2;
  428.         if (mode == 0 && !dont_sort_folders)
  429.         sort_articles(-1);
  430.         else if (mode == 1) {
  431.         article_number n;
  432.         for (n = 0; n < n_articles; n++) {
  433.             ah = articles[n];
  434.             if (n == 0)
  435.             ah->flag |= A_ROOT_ART;
  436.             else if (strcmp(ah->sender, articles[n-1]->sender) == 0)
  437.             articles[n-1]->flag |= A_NEXT_SAME;
  438.             else
  439.             ah->flag |= A_ROOT_ART;
  440.         }
  441.         bypass_consolidation = consolidated_manual ? 2 : 1;
  442.         } else
  443.         no_sort_articles();
  444.     } else
  445.         no_sort_articles();
  446.  
  447.     cc_save = cancel_count;
  448.     cancel_count = 0;
  449.     if (mode == 1) {
  450.         fmt_linenum = -1;
  451.         header_lines = "*";
  452.     }
  453.  
  454.      reenter_menu:
  455.         ignore_fancy_select = 1;
  456.     menu_cmd = menu(folder_header);
  457.         ignore_fancy_select = 0;
  458.  
  459.     if (mode == 0 && cancel_count) {
  460.         register article_number cur;
  461.  
  462.         cancel_count = 0;
  463.         for (cur = 0; cur < n_articles; cur++) {
  464.         if (articles[cur]->attr == A_CANCEL) cancel_count++;
  465.         }
  466.     }
  467.     if (mode == 0 && cancel_count) {
  468.         clrdisp();
  469.         tprintf("\rFolder: %s\n\rFile:   %s\n\n\r", folder_name, folder_file);
  470.         if (cancel_count == n_articles)
  471.         tprintf("Cancel all articles and remove folder? ");
  472.         else
  473.         tprintf("Remove %d article%s from folder? ",
  474.                cancel_count, plural((long)cancel_count));
  475.         fl;
  476.  
  477.         switch (yes(1)) {
  478.          case 1:
  479.         tprintf("\n\n");
  480.         if (cancel_count == n_articles) {
  481.             if (unlink(group_path_name) < 0 &&
  482.             nn_truncate(group_path_name, (off_t)0) < 0) {
  483.             tprintf("\rCould not unlink %s\n\r", group_path_name);
  484.             any_key(0);
  485.             }
  486.         } else
  487.             rewrite_folder();
  488.         break;
  489.          case 0:
  490.         break;
  491.          default:
  492.         goto reenter_menu;
  493.         }
  494.     }
  495.     cancel_count = cc_save;
  496.     }
  497.  
  498.     release_memory(&mem_marker);
  499.     if (fmt_linenum == -1) {
  500.     fmt_linenum = orig_layout;
  501.     header_lines = orig_hdr_lines;
  502.     }
  503.  
  504.     if (was_raw) nn_raw();
  505.  
  506.     return menu_cmd;
  507. }
  508.  
  509. void
  510. rewrite_folder()
  511. {
  512.     register FILE *src, *dst;
  513.     char *oldfile;
  514.     register int c;
  515.     register long cnt;
  516.     register article_header *ah, **ahp;
  517.     register article_number n;
  518.  
  519.     if (strchr(backup_folder_path, '/'))
  520.     oldfile = backup_folder_path;
  521.     else
  522.     oldfile = relative(nn_directory, backup_folder_path);
  523.  
  524.     if (move_file(group_path_name, oldfile, 1) < 0) {
  525.     tprintf("\r\n\nCannot backup folder in %s\n", oldfile);
  526.     goto confirm;
  527.     }
  528.  
  529.     if ((src = open_file(oldfile, OPEN_READ)) == NULL) {
  530.     tprintf("\rCannot open %s\n\r", oldfile);
  531.     goto move_back;
  532.     }
  533.  
  534.     if ((dst = open_file(group_path_name, OPEN_CREATE)) == NULL) {
  535.     fclose(src);
  536.     tprintf("\rCannot create %s\n\r", group_path_name);
  537.     goto move_back;
  538.     }
  539.  
  540.     sort_articles(-2);
  541.  
  542.     tprintf("\rCompressing folder...\n\r"); fl;
  543.  
  544.     get_folder_type(src);
  545.  
  546.     for (ahp = articles, n = n_articles; --n >= 0; ahp++) {
  547.     ah = *ahp;
  548.     cnt = ah->lpos - ah->hpos;
  549.     if (folder_rewrite_trace)
  550.         tprintf("%s\t%s (%ld-%ld=%ld)\n\r",
  551.            ah->attr == A_CANCEL ? "CANCEL" : "KEEP",
  552.            ah->subject, (long)(ah->hpos), (long)(ah->lpos), cnt);
  553.     if (ah->attr == A_CANCEL) continue;
  554.     fseek(src, ah->hpos, 0);
  555.     mailbox_format(dst, -1);
  556.     while (--cnt >= 0) {
  557.         if ((c = getc(src)) == EOF) break;
  558.         putc(c, dst);
  559.     }
  560.     mailbox_format(dst, 0);
  561.     }
  562.  
  563.     fclose(src);
  564.     if (fclose(dst) == EOF) goto move_back;
  565.     if (!keep_backup_folder) unlink(oldfile);
  566.     if (folder_rewrite_trace) goto confirm;
  567.     return;
  568.  
  569.  move_back:
  570.     if (move_file(oldfile, group_path_name, 2) == 0)
  571.     tprintf("Cannot create new file -- Folder restored\n\r");
  572.     else
  573.     tprintf("Cannot create new file\n\n\rFolder saved in %s\n\r", oldfile);
  574.  
  575.  confirm:
  576.     any_key(0);
  577. }
  578.