1 /*
2  *      lo_fns.c - Line operations, remove duplicate lines, empty lines,
3  *                 lines with only whitespace, sort lines.
4  *
5  *      Copyright 2015 Sylvan Mostert <smostert.dev@gmail.com>
6  *
7  *      This program is free software; you can redistribute it and/or modify
8  *      it under the terms of the GNU General Public License as published by
9  *      the Free Software Foundation; either version 2 of the License, or
10  *      (at your option) any later version.
11  *
12  *      This program is distributed in the hope that it will be useful,
13  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *      GNU General Public License for more details.
16  *
17  *      You should have received a copy of the GNU General Public License along
18  *      with this program; if not, write to the Free Software Foundation, Inc.,
19  *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21 
22 
23 #include "lo_fns.h"
24 #include "lo_prefs.h"
25 
26 
27 /* Get sort function based on user preferences */
28 lo_strcmpfns
getcmpfns(void)29 getcmpfns(void)
30 {
31 	if(lo_info->use_collation_compare)
32 	{
33 		return g_utf8_collate;
34 	}
35 	else
36 	{
37 		return g_strcmp0;
38 	}
39 }
40 
41 
42 /* comparison function to be used in qsort */
43 static gint
compare_asc(const void * a,const void * b)44 compare_asc(const void *a, const void *b)
45 {
46 	lo_strcmpfns lo_strcmp = getcmpfns();
47 	return lo_strcmp(*(const gchar **)a, *(const gchar **)b);
48 }
49 
50 
51 /* comparison function to be used in qsort */
52 static gint
compare_desc(const void * a,const void * b)53 compare_desc(const void *a, const void *b)
54 {
55 	lo_strcmpfns lo_strcmp = getcmpfns();
56 	return lo_strcmp(*(const gchar **)b, *(const gchar **)a);
57 }
58 
59 
60 /* Remove Duplicate Lines, sorted */
61 gint
rmdupst(gchar ** lines,gint num_lines,gchar * new_file)62 rmdupst(gchar **lines, gint num_lines, gchar *new_file)
63 {
64 	gchar *nf_end  = new_file;     /* points to last char of new_file */
65 	gchar *lineptr = (gchar *)" "; /* temporary line pointer */
66 	gint  i        = 0;            /* iterator */
67 	gint  changed  = 0;            /* number of lines removed */
68 	lo_strcmpfns lo_strcmp = getcmpfns();
69 
70 	/* sort **lines ascending */
71 	qsort(lines, num_lines, sizeof(gchar *), compare_asc);
72 
73 	/* loop through **lines, join first occurances into one str (new_file) */
74 	for (i = 0; i < num_lines; i++)
75 	{
76 		if (lo_strcmp(lines[i], lineptr) != 0)
77 		{
78 			changed++;     /* number of lines kept */
79 			lineptr = lines[i];
80 			nf_end  = g_stpcpy(nf_end, lines[i]);
81 		}
82 	}
83 
84 	/* return the number of lines deleted */
85 	return -(num_lines - changed);
86 }
87 
88 
89 /* Remove Duplicate Lines, ordered */
90 gint
rmdupln(gchar ** lines,gint num_lines,gchar * new_file)91 rmdupln(gchar **lines, gint num_lines, gchar *new_file)
92 {
93 	gchar *nf_end  = new_file;  /* points to last char of new_file */
94 	gint  i        = 0;         /* iterator */
95 	gint  j        = 0;         /* iterator */
96 	gboolean *to_remove = NULL; /* flag to 'mark' which lines to remove */
97 	gint  changed  = 0;         /* number of lines removed */
98 	lo_strcmpfns lo_strcmp = getcmpfns();
99 
100 
101 	/* allocate and set *to_remove to all FALSE
102 	 * to_remove[i] represents whether lines[i] should be removed  */
103 	to_remove = g_malloc(sizeof(gboolean) * num_lines);
104 	for (i = 0; i < (num_lines); i++)
105 		to_remove[i] = FALSE;
106 
107 	/* find which **lines are duplicate, and mark them as duplicate */
108 	for (i = 0; i < num_lines; i++)  /* loop through **lines */
109 	{
110 		/* make sure that the line is not already duplicate */
111 		if (!to_remove[i])
112 		{
113 			/* find the rest of same lines */
114 			for (j = (i + 1); j < num_lines; j++)
115 			{
116 				if (!to_remove[j] && lo_strcmp(lines[i], lines[j]) == 0)
117 					to_remove[j] = TRUE; /* line is duplicate, mark to remove */
118 			}
119 		}
120 	}
121 
122 	/* copy **lines into 'new_file' if it is not FALSE (not duplicate) */
123 	for (i = 0; i < num_lines; i++)
124 		if (!to_remove[i])
125 		{
126 			changed++;     /* number of lines kept */
127 			nf_end = g_stpcpy(nf_end, lines[i]);
128 		}
129 
130 	/* free used memory */
131 	g_free(to_remove);
132 
133 	/* return the number of lines deleted */
134 	return -(num_lines - changed);
135 }
136 
137 
138 /* Remove Unique Lines */
139 gint
rmunqln(gchar ** lines,gint num_lines,gchar * new_file)140 rmunqln(gchar **lines, gint num_lines, gchar *new_file)
141 {
142 	gchar *nf_end = new_file;   /* points to last char of new_file */
143 	gint  i       = 0;          /* iterator */
144 	gint  j       = 0;          /* iterator */
145 	gboolean *to_remove = NULL; /* to 'mark' which lines to remove */
146 	gint  changed = 0;          /* number of lines removed */
147 	lo_strcmpfns lo_strcmp = getcmpfns();
148 
149 
150 	/* allocate and set *to_remove to all TRUE
151 	 * to_remove[i] represents whether lines[i] should be removed */
152 	to_remove = g_malloc(sizeof(gboolean) * num_lines);
153 	for (i = 0; i < num_lines; i++)
154 		to_remove[i] = TRUE;
155 
156 	/* find all unique lines and set them to FALSE (not to be removed) */
157 	for (i = 0; i < num_lines; i++)
158 		/* make sure that the line is not already determined to be unique */
159 		if (to_remove[i])
160 			for (j = (i + 1); j < num_lines; j++)
161 				if (to_remove[j] && lo_strcmp(lines[i], lines[j]) == 0)
162 				{
163 					to_remove[i] = FALSE;
164 					to_remove[j] = FALSE;
165 				}
166 
167 	/* copy **lines into 'new_file' if it is not FALSE(not duplicate) */
168 	for (i = 0; i < num_lines; i++)
169 		if (!to_remove[i])
170 		{
171 			changed++;     /* number of lines kept */
172 			nf_end = g_stpcpy(nf_end, lines[i]);
173 		}
174 
175 	/* free used memory */
176 	g_free(to_remove);
177 
178 	/* return the number of lines deleted */
179 	return -(num_lines - changed);
180 }
181 
182 
183 /* Keep Unique Lines */
184 gint
kpunqln(gchar ** lines,gint num_lines,gchar * new_file)185 kpunqln(gchar **lines, gint num_lines, gchar *new_file)
186 {
187 	gchar *nf_end = new_file;   /* points to last char of new_file */
188 	gint  i       = 0;          /* iterator */
189 	gint  j       = 0;          /* iterator */
190 	gboolean *to_remove = NULL; /* to 'mark' which lines to remove */
191 	gint  changed = 0;          /* number of lines removed */
192 	lo_strcmpfns lo_strcmp = getcmpfns();
193 
194 
195 	/* allocate and set *to_remove to all FALSE
196 	 * to_remove[i] represents whether lines[i] should be removed */
197 	to_remove = g_malloc(sizeof(gboolean) * num_lines);
198 	for (i = 0; i < num_lines; i++)
199 		to_remove[i] = FALSE;
200 
201 	/* find all non unique lines and set them to TRUE (to be removed) */
202 	for (i = 0; i < num_lines; i++)
203 		/* make sure that the line is not already determined to be non unique */
204 		if (!to_remove[i])
205 			for (j = (i + 1); j < num_lines; j++)
206 				if (!to_remove[j] && lo_strcmp(lines[i], lines[j]) == 0)
207 				{
208 					to_remove[i] = TRUE;
209 					to_remove[j] = TRUE;
210 				}
211 
212 	/* copy **lines into 'new_file' if it is not FALSE(not duplicate) */
213 	for (i = 0; i < num_lines; i++)
214 		if (!to_remove[i])
215 		{
216 			changed++;     /* number of lines kept */
217 			nf_end = g_stpcpy(nf_end, lines[i]);
218 		}
219 
220 	/* free used memory */
221 	g_free(to_remove);
222 
223 	/* return the number of lines deleted */
224 	return -(num_lines - changed);
225 }
226 
227 
228 /* Remove Empty Lines */
229 gint
rmemtyln(ScintillaObject * sci,gint line_num,gint end_line_num)230 rmemtyln(ScintillaObject *sci, gint line_num, gint end_line_num)
231 {
232 	gint changed = 0;     /* number of lines removed */
233 
234 	while (line_num <= end_line_num)    /* loop through lines */
235 	{
236 		/* check if the first posn of the line is also the end of line posn */
237 		if (sci_get_position_from_line(sci, line_num) ==
238 			sci_get_line_end_position (sci, line_num))
239 		{
240 			scintilla_send_message(sci,
241 								   SCI_DELETERANGE,
242 								   sci_get_position_from_line(sci, line_num),
243 								   sci_get_line_length(sci, line_num));
244 
245 			line_num--;
246 			end_line_num--;
247 			changed++;
248 		}
249 		line_num++;
250 	}
251 
252 	/* return the number of lines deleted */
253 	return -changed;
254 }
255 
256 
257 /* Remove Whitespace Lines */
258 gint
rmwhspln(ScintillaObject * sci,gint line_num,gint end_line_num)259 rmwhspln(ScintillaObject *sci, gint line_num, gint end_line_num)
260 {
261 	gint indent;                       /* indent position */
262 	gint changed = 0;                  /* number of lines removed */
263 
264 	while (line_num <= end_line_num)    /* loop through lines */
265 	{
266 		indent = scintilla_send_message(sci,
267 										SCI_GETLINEINDENTPOSITION,
268 										line_num, 0);
269 
270 		/* check if the posn of indentation is also the end of line posn */
271 		if (indent -
272 				sci_get_position_from_line(sci, line_num) ==
273 				sci_get_line_end_position (sci, line_num) -
274 				sci_get_position_from_line(sci, line_num))
275 		{
276 			scintilla_send_message(sci,
277 								   SCI_DELETERANGE,
278 								   sci_get_position_from_line(sci, line_num),
279 								   sci_get_line_length(sci, line_num));
280 
281 			line_num--;
282 			end_line_num--;
283 			changed++;
284 		}
285 		line_num++;
286 	}
287 
288 	/* return the number of lines deleted */
289 	return -changed;
290 }
291 
292 
293 /* Sort Lines Ascending */
294 gint
sortlnsasc(gchar ** lines,gint num_lines,gchar * new_file)295 sortlnsasc(gchar **lines, gint num_lines, gchar *new_file)
296 {
297 	gchar *nf_end = new_file;          /* points to last char of new_file */
298 	gint i;
299 
300 	qsort(lines, num_lines, sizeof(gchar *), compare_asc);
301 
302 	/* join **lines into one string (new_file) */
303 	for (i = 0; i < num_lines; i++)
304 		nf_end = g_stpcpy(nf_end, lines[i]);
305 
306 	return num_lines;
307 }
308 
309 
310 /* Sort Lines Descending */
311 gint
sortlndesc(gchar ** lines,gint num_lines,gchar * new_file)312 sortlndesc(gchar **lines, gint num_lines, gchar *new_file)
313 {
314 	gchar *nf_end = new_file;          /* points to last char of new_file */
315 	gint i;
316 
317 	qsort(lines, num_lines, sizeof(gchar *), compare_desc);
318 
319 	/* join **lines into one string (new_file) */
320 	for (i = 0; i < num_lines; i++)
321 		nf_end = g_stpcpy(nf_end, lines[i]);
322 
323 	return num_lines;
324 }
325 
326 
327 /* Remove Every Nth Line */
328 gint
rmnthln(ScintillaObject * sci,gint line_num,gint end_line_num)329 rmnthln(ScintillaObject *sci, gint line_num, gint end_line_num)
330 {
331 	gboolean ok;
332 	gdouble n;
333 	gint count;
334 	gint changed = 0;     /* number of lines removed */
335 
336 	ok = dialogs_show_input_numeric(_("Remove every Nth line"),
337 									_("Value of N"), &n, 1, 1000, 1);
338 	if (ok == FALSE)
339 	{
340 		return 0;
341 	}
342 
343 	count = n;
344 	while (line_num <= end_line_num)    /* loop through lines */
345 	{
346 		count--;
347 
348 		/* check if this is the nth line. */
349 		if (count == 0)
350 		{
351 			scintilla_send_message(sci,
352 								   SCI_DELETERANGE,
353 								   sci_get_position_from_line(sci, line_num),
354 								   sci_get_line_length(sci, line_num));
355 
356 			line_num--;
357 			end_line_num--;
358 			changed++;
359 			count = n;
360 		}
361 		line_num++;
362 	}
363 
364 	/* return the number of lines deleted */
365 	return -changed;
366 }
367