* Update to version 2.19.5
[alpine.git] / pith / addrbook.c
blobfa6507bf5a54351cc5ecd8a421d361234ac46bf2
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: addrbook.c 90 2006-07-19 22:30:36Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2014 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 addrbook.c
21 display format/support routines
22 ====*/
24 #include "../c-client/c-client.h"
26 #include <system.h>
27 #include <general.h>
29 #include "addrbook.h"
30 #include "state.h"
31 #include "adrbklib.h"
32 #include "abdlc.h"
33 #include "conf.h"
34 #include "conftype.h"
35 #include "status.h"
36 #include "debug.h"
37 #include "osdep/collate.h"
40 /* internal prototypes */
41 void parse_format(char *, COL_S *);
47 * We have to call this to set up the format of the columns. There is a
48 * separate format for each addrbook, so we need to call this for each
49 * addrbook. We call it when the pab's are built. It also depends on
50 * whether or not as.checkboxes is set, so if we go into a Select mode
51 * from the address book maintenance screen we need to re-call this. Since
52 * we can't go back out of ListMode we don't have that problem. Restore_state
53 * has to call it because of the as.checkboxes possibly being different in
54 * the two states.
56 void
57 addrbook_new_disp_form(PerAddrBook *pab, char **list, int addrbook_num,
58 int (*prefix_f)(PerAddrBook *, int *))
60 char *last_one;
61 int column = 0;
63 dprint((9, "- init_disp_form(%s) -\n",
64 (pab && pab->abnick) ? pab->abnick : "?"));
66 memset((void *)pab->disp_form, 0, NFIELDS*sizeof(COL_S));
67 pab->disp_form[1].wtype = WeCalculate; /* so we don't get false AllAuto */
69 if(prefix_f)
70 as.do_bold = (*prefix_f)(pab, &column);
72 /* if custom format is specified */
73 if(list && list[0] && list[0][0]){
74 /* find the one for addrbook_num */
75 for(last_one = *list;
76 *list != NULL && addrbook_num;
77 addrbook_num--,list++)
78 last_one = *list;
80 /* If not enough to go around, last one repeats */
81 if(*list == NULL)
82 parse_format(last_one, &(pab->disp_form[column]));
83 else
84 parse_format(*list, &(pab->disp_form[column]));
86 else{ /* default */
87 /* If 2nd wtype is AllAuto, the widths are calculated old way */
88 pab->disp_form[1].wtype = AllAuto;
90 pab->disp_form[column++].type = Nickname;
91 pab->disp_form[column++].type = Fullname;
92 pab->disp_form[column++].type = Addr;
93 /* Fill in rest */
94 while(column < NFIELDS)
95 pab->disp_form[column++].type = Notused;
100 struct parse_tokens {
101 char *name;
102 ColumnType ctype;
105 struct parse_tokens ptokens[] = {
106 {"NICKNAME", Nickname},
107 {"FULLNAME", Fullname},
108 {"ADDRESS", Addr},
109 {"FCC", Filecopy},
110 {"COMMENT", Comment},
111 {"DEFAULT", Def},
112 {NULL, Notused}
116 * Parse format_str and fill in disp_form structure based on what's there.
118 * Args: format_str -- The format string from pinerc.
119 * disp_form -- This is where we fill in the answer.
121 * The format string consists of special tokens which give the order of
122 * the columns to be displayed. The possible tokens are NICKNAME,
123 * FULLNAME, ADDRESS, FCC, COMMENT. If a token is followed by
124 * parens with an integer inside (FULLNAME(16)) then that means we
125 * make that variable that many characters wide. If it is a percentage, we
126 * allocate that percentage of the columns to that variable. If no
127 * parens, that means we calculate it for the user. The tokens are
128 * delimited by white space. A token of DEFAULT means to calculate the
129 * whole thing as we would if no spec was given. This makes it possible
130 * to specify default for one addrbook and something special for another.
132 void
133 parse_format(char *format_str, COL_S *disp_form)
135 int column = 0;
136 char *p, *q;
137 struct parse_tokens *pt;
138 int nicknames, fullnames, addresses, not_allauto;
139 int warnings = 0;
141 p = format_str;
142 while(p && *p && column < NFIELDS){
143 p = skip_white_space(p); /* space for next word */
145 /* look for the ptoken this word matches */
146 for(pt = ptokens; pt->name; pt++)
147 if(!struncmp(pt->name, p, strlen(pt->name)))
148 break;
150 /* ignore unrecognized word */
151 if(!pt->name){
152 char *r;
154 if((r=strindex(p, SPACE)) != NULL)
155 *r = '\0';
157 dprint((2, "parse_format: ignoring unrecognized word \"%s\" in address-book-formats\n", p ? p : "?"));
158 q_status_message1(SM_ORDER, warnings++==0 ? 1 : 0, 4,
159 /* TRANSLATORS: an informative error message */
160 _("Ignoring unrecognized word \"%s\" in Address-Book-Formats"), p);
161 /* put back space */
162 if(r)
163 *r = SPACE;
165 /* skip unrecognized word */
166 while(p && *p && !isspace((unsigned char)(*p)))
167 p++;
169 continue;
172 disp_form[column].type = pt->ctype;
174 /* skip over name and look for parens */
175 p += strlen(pt->name);
176 if(*p == '('){
177 p++;
178 q = p;
179 while(p && *p && isdigit((unsigned char)*p))
180 p++;
182 if(p && *p && *p == ')' && p > q){
183 disp_form[column].wtype = Fixed;
184 disp_form[column].req_width = atoi(q);
186 else if(p && *p && *p == '%' && p > q){
187 disp_form[column].wtype = Percent;
188 disp_form[column].req_width = atoi(q);
190 else{
191 disp_form[column].wtype = WeCalculate;
192 if(disp_form[column].type == Nickname)
193 disp_form[column].req_width = 8;
194 else
195 disp_form[column].req_width = 3;
198 else{
199 disp_form[column].wtype = WeCalculate;
200 if(disp_form[column].type == Nickname)
201 disp_form[column].req_width = 8;
202 else
203 disp_form[column].req_width = 3;
206 if(disp_form[column].type == Def){
207 /* If any type is DEFAULT, the widths are calculated old way */
208 assign_default:
209 column = 0;
211 disp_form[column].wtype = AllAuto;
212 disp_form[column++].type = Nickname;
213 disp_form[column].wtype = AllAuto;
214 disp_form[column++].type = Fullname;
215 disp_form[column].wtype = AllAuto;
216 disp_form[column++].type = Addr;
217 /* Fill in rest */
218 while(column < NFIELDS)
219 disp_form[column++].type = Notused;
221 return;
224 column++;
225 /* skip text at end of word */
226 while(p && *p && !isspace((unsigned char)(*p)))
227 p++;
230 if(column == 0){
231 q_status_message(SM_ORDER, 0, 4,
232 _("Address-Book-Formats has no recognizable words, using default format"));
233 goto assign_default;
236 /* Fill in rest */
237 while(column < NFIELDS)
238 disp_form[column++].type = Notused;
240 /* check to see if user is just re-ordering default fields */
241 nicknames = 0;
242 fullnames = 0;
243 addresses = 0;
244 not_allauto = 0;
245 for(column = 0; column < NFIELDS; column++){
246 if(disp_form[column].type != Notused
247 && disp_form[column].wtype != WeCalculate)
248 not_allauto++;
250 switch(disp_form[column].type){
251 case Nickname:
252 nicknames++;
253 break;
255 case Fullname:
256 fullnames++;
257 break;
259 case Addr:
260 addresses++;
261 break;
263 case Filecopy:
264 case Comment:
265 not_allauto++;
266 break;
268 default:
269 break;
274 * Special case: if there is no address field specified, we put in
275 * a special field called WhenNoAddrDisplayed, which causes list
276 * entries to be displayable in all cases.
278 if(!addresses){
279 for(column = 0; column < NFIELDS; column++)
280 if(disp_form[column].type == Notused)
281 break;
283 if(column < NFIELDS){
284 disp_form[column].type = WhenNoAddrDisplayed;
285 disp_form[column].wtype = Special;
289 if(nicknames == 1 && fullnames == 1 && addresses == 1 && not_allauto == 0)
290 disp_form[0].wtype = AllAuto; /* set to do default widths */
295 * Find the first selectable line greater than or equal to line. That is,
296 * the first line the cursor is allowed to start on.
297 * (If there are none >= line, it will find the highest one.)
299 * Returns the line number of the found line or NO_LINE if there isn't one.
301 long
302 first_selectable_line(long int line)
304 long lineno;
305 register PerAddrBook *pab;
306 int i;
308 /* skip past non-selectable lines */
309 for(lineno=line;
310 !line_is_selectable(lineno) && dlist(lineno)->type != End;
311 lineno++)
312 ;/* do nothing */
314 if(line_is_selectable(lineno))
315 return(lineno);
318 * There were no selectable lines from lineno on down. Trying looking
319 * back up the list.
321 for(lineno=line-1;
322 !line_is_selectable(lineno) && dlist(lineno)->type != Beginning;
323 lineno--)
324 ;/* do nothing */
326 if(line_is_selectable(lineno))
327 return(lineno);
330 * No selectable lines at all.
331 * If some of the addrbooks are still not displayed, it is too
332 * early to set the no_op_possbl flag. Or, if some of the addrbooks
333 * are empty but writable, then we should not set it either.
335 for(i = 0; i < as.n_addrbk; i++){
336 pab = &as.adrbks[i];
337 if(pab->ostatus != Open &&
338 pab->ostatus != HalfOpen &&
339 pab->ostatus != ThreeQuartOpen)
340 return NO_LINE;
342 if(pab->access == ReadWrite && adrbk_count(pab->address_book) == 0)
343 return NO_LINE;
346 as.no_op_possbl++;
347 return NO_LINE;
352 * Returns 1 if this line is of a type that can have a cursor on it.
355 line_is_selectable(long int lineno)
357 register AddrScrn_Disp *dl;
359 if((dl = dlist(lineno)) && (dl->type == Text ||
360 dl->type == ListEmpty ||
361 dl->type == TitleCmb ||
362 dl->type == Beginning ||
363 dl->type == End)){
365 return 0;
368 return 1;