/* Return the index of the first non-whitespace or newline character in STRING. */ int skip_whitespace_and_newlines (char *string) { register int i; for (i = 0; string && whitespace_or_newline (string[i]); i++); return i; }
/* Return the index of the first non-node character in STRING. Note that this function contains quite a bit of hair to ignore periods in some special cases. This is because we here at GNU ship some info files which contain nodenames that contain periods. No such nodename can start with a period, or continue with whitespace, newline, or ')' immediately following the period. If second argument NEWLINES_OKAY is non-zero, newlines should be skipped while parsing out the nodename specification. */ int skip_node_characters (char *string, int newlines_okay) { register int c, i = 0; int paren_seen = 0; int paren = 0; /* Handle special case. This is when another function has parsed out the filename component of the node name, and we just want to parse out the nodename proper. In that case, a period at the start of the nodename indicates an empty nodename. */ if (string && *string == '.') return 0; if (string && *string == '(') { paren++; paren_seen++; i++; } for (; string && (c = string[i]); i++) { if (paren) { if (c == '(') paren++; else if (c == ')') paren--; continue; } /* If the character following the close paren is a space or period, then this node name has no more characters associated with it. */ if (c == '\t' || c == ',' || c == INFO_TAGSEP || ((!newlines_okay) && (c == '\n')) || ((paren_seen && string[i - 1] == ')') && (c == ' ' || c == '.')) || (c == '.' && ( #if 0 /* This test causes a node name ending in a period, like `This.', not to be found. The trailing . is stripped. This occurs in the jargon file (`I see no X here.' is a node name). */ (!string[i + 1]) || #endif (whitespace_or_newline (string[i + 1])) || (string[i + 1] == ')')))) break; } return i; }
/* Search for sequences of whitespace or newlines in STRING, replacing all such sequences with just a single space. Remove whitespace from start and end of string. */ void canonicalize_whitespace (char *string) { register int i, j; int len, whitespace_found, whitespace_loc = 0; char *temp; if (!string) return; len = strlen (string); temp = xmalloc (1 + len); /* Search for sequences of whitespace or newlines. Replace all such sequences in the string with just a single space. */ whitespace_found = 0; for (i = 0, j = 0; string[i]; i++) { if (whitespace_or_newline (string[i])) { whitespace_found++; whitespace_loc = i; continue; } else { if (whitespace_found && whitespace_loc) { whitespace_found = 0; /* Suppress whitespace at start of string. */ if (j) temp[j++] = ' '; } temp[j++] = string[i]; } } /* Kill trailing whitespace. */ if (j && whitespace (temp[j - 1])) j--; temp[j] = '\0'; strcpy (string, temp); free (temp); }
/* Given CONTENTS and FB (a file buffer), add the menu found in CONTENTS to the menu found in FB->contents. Second argument SIZE is the total size of CONTENTS. */ static void add_menu_to_file_buffer (char *contents, size_t size, FILE_BUFFER *fb) { SEARCH_BINDING contents_binding, fb_binding; long contents_offset, fb_offset; contents_binding.buffer = contents; contents_binding.start = 0; contents_binding.end = size; contents_binding.flags = S_FoldCase | S_SkipDest; fb_binding.buffer = fb->contents; fb_binding.start = 0; fb_binding.end = fb->filesize; fb_binding.flags = S_FoldCase | S_SkipDest; /* Move to the start of the menus in CONTENTS and FB. */ if (search_forward (INFO_MENU_LABEL, &contents_binding, &contents_offset) != search_success) /* If there is no menu in CONTENTS, quit now. */ return; /* There is a menu in CONTENTS, and contents_offset points to the first character following the menu starter string. Skip all whitespace and newline characters. */ contents_offset += skip_whitespace_and_newlines (contents + contents_offset); /* If there is no menu in FB, make one. */ if (search_forward (INFO_MENU_LABEL, &fb_binding, &fb_offset) != search_success) { /* Find the start of the second node in this file buffer. If there is only one node, we will be adding the contents to the end of this node. */ fb_offset = find_node_separator (&fb_binding); /* If not even a single node separator, give up. */ if (fb_offset == -1) return; fb_binding.start = fb_offset; fb_binding.start += skip_node_separator (fb_binding.buffer + fb_binding.start); /* Try to find the next node separator. */ fb_offset = find_node_separator (&fb_binding); /* If found one, consider that the start of the menu. Otherwise, the start of this menu is the end of the file buffer (i.e., fb->size). */ if (fb_offset != -1) fb_binding.start = fb_offset; else fb_binding.start = fb_binding.end; insert_text_into_fb_at_binding (fb, &fb_binding, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)); fb_binding.buffer = fb->contents; fb_binding.start = 0; fb_binding.end = fb->filesize; if (search_forward (INFO_MENU_LABEL, &fb_binding, &fb_offset) != search_success) abort (); } /* CONTENTS_OFFSET and FB_OFFSET point to the starts of the menus that appear in their respective buffers. Add the remainder of CONTENTS to the end of FB's menu. */ fb_binding.start = fb_offset; fb_offset = find_node_separator (&fb_binding); if (fb_offset != -1) fb_binding.start = fb_offset; else fb_binding.start = fb_binding.end; /* Leave exactly one blank line between directory entries. */ { int num_found = 0; while ((fb_binding.start > 0) && (whitespace_or_newline (fb_binding.buffer[fb_binding.start - 1]))) { num_found++; fb_binding.start--; } /* Optimize if possible. */ if (num_found >= 2) { fb_binding.buffer[fb_binding.start++] = '\n'; fb_binding.buffer[fb_binding.start++] = '\n'; } else { /* Do it the hard way. */ insert_text_into_fb_at_binding (fb, &fb_binding, "\n\n", 2); fb_binding.start += 2; } } /* Insert the new menu. */ insert_text_into_fb_at_binding (fb, &fb_binding, contents + contents_offset, size - contents_offset); }