prntfile.cpp 9.05 KB
Newer Older
1
/* prntfile.cpp */
2
// vi: tabstop=4
3
4
5

/* Synchronet file print/display routines */

6
/* $Id: prntfile.cpp,v 1.47 2020/05/26 03:07:05 rswindell Exp $ */
7
8
9
10
11

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
12
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
40
#include "utf8.h"
41

42
#ifndef PRINTFILE_MAX_LINE_LEN
43
#define PRINTFILE_MAX_LINE_LEN (8*1024)
44
#endif
45
#ifndef PRINTFILE_MAX_FILE_LEN
46
#define PRINTFILE_MAX_FILE_LEN (2*1024*1024)
47
#endif
48

49
50
51
52
53
/****************************************************************************/
/* Prints a file remotely and locally, interpreting ^A sequences, checks    */
/* for pauses, aborts and ANSI. 'str' is the path of the file to print      */
/* Called from functions menu and text_sec                                  */
/****************************************************************************/
54
bool sbbs_t::printfile(const char* fname, long mode, long org_cols, JSObject* obj)
55
{
56
	char* buf;
57
	char fpath[MAX_PATH+1];
58
	char* p;
rswindell's avatar
rswindell committed
59
	int file;
60
	BOOL rip=FALSE;
rswindell's avatar
rswindell committed
61
	long l,length,savcon=console;
62
63
	FILE *stream;

64
65
66
	SAFECOPY(fpath, fname);
	fexistcase(fpath);
	p=getfext(fpath);
67
	if(p!=NULL) {
68
		if(stricmp(p,".rip")==0) {
rswindell's avatar
rswindell committed
69
			rip=TRUE;
rswindell's avatar
rswindell committed
70
			mode|=P_NOPAUSE;
71
72
		} else if(stricmp(p, ".seq") == 0) {
			mode |= P_PETSCII;
73
74
		} else if(stricmp(p, ".utf8") == 0) {
			mode |= P_UTF8;
rswindell's avatar
rswindell committed
75
		}
76
	}
77

78
	if(mode&P_NOABORT || rip) {
79
80
		if(online==ON_REMOTE && console&CON_R_ECHO) {
			rioctl(IOCM|ABORT);
rswindell's avatar
rswindell committed
81
82
83
84
			rioctl(IOCS|ABORT); 
		}
		sys_status&=~SS_ABORT; 
	}
85

rswindell's avatar
rswindell committed
86
87
	if(!(mode&P_NOCRLF) && row > 0 && !rip) {
		newline();
88
	}
89

90
	if((stream=fnopen(&file,fpath,O_RDONLY|O_DENYNONE))==NULL) {
91
92
		if(!(mode&P_NOERROR)) {
			lprintf(LOG_NOTICE,"!Error %d (%s) opening: %s"
93
				,errno,strerror(errno),fpath);
94
			bputs(text[FileNotFound]);
95
			if(SYSOP) bputs(fpath);
96
97
98
			CRLF;
		}
		return false; 
rswindell's avatar
rswindell committed
99
	}
100

101
	length=(long)filelength(file);
102
	if(length < 1) {
rswindell's avatar
rswindell committed
103
		fclose(stream);
104
105
106
107
108
		if(length < 0) {
			errormsg(WHERE,ERR_CHK,fpath,length);
			return false;
		}
		return true;
rswindell's avatar
rswindell committed
109
	}
110

111
	if((mode&P_OPENCLOSE) && length <= PRINTFILE_MAX_FILE_LEN) {
112
113
114
115
116
117
118
119
120
121
122
123
124
		if((buf=(char*)malloc(length+1L))==NULL) {
			fclose(stream);
			errormsg(WHERE,ERR_ALLOC,fpath,length+1L);
			return false; 
		}
		l=lread(file,buf,length);
		fclose(stream);
		if(l!=length)
			errormsg(WHERE,ERR_READ,fpath,length);
		else {
			buf[l]=0;
			if((mode&P_UTF8) && !term_supports(UTF8))
				utf8_normalize_str(buf);
125
			putmsg(buf,mode,org_cols, obj);
126
127
128
		}
		free(buf);
	} else {	// Line-at-a-time mode
129
130
131
		uint tmpatr = curatr;
		if(!(mode&P_SAVEATR))
			attr(LIGHTGRAY);
132
133
134
135
136
137
138
139
		if(length > PRINTFILE_MAX_LINE_LEN)
			length = PRINTFILE_MAX_LINE_LEN;
		if((buf=(char*)malloc(length+1L))==NULL) {
			fclose(stream);
			errormsg(WHERE,ERR_ALLOC,fpath,length+1L);
			return false; 
		}
		while(!feof(stream) && !msgabort()) {
140
			if(fgets(buf, length + 1, stream) == NULL)
141
142
143
				break;
			if((mode&P_UTF8) && !term_supports(UTF8))
				utf8_normalize_str(buf);
144
			if(putmsg(buf, mode|P_SAVEATR, org_cols, obj) != '\0') // early-EOF?
145
				break;
146
147
		}
		free(buf);
rswindell's avatar
rswindell committed
148
		fclose(stream);
149
150
		if(!(mode&P_SAVEATR))
			attr(tmpatr);
rswindell's avatar
rswindell committed
151
	}
152

153
	if((mode&P_NOABORT || rip) && online==ON_REMOTE) {
154
		SYNC;
rswindell's avatar
rswindell committed
155
156
		rioctl(IOSM|ABORT); 
	}
157
158
159
	if(rip)
		ansi_getlines();
	console=savcon;
160
	return true;
rswindell's avatar
rswindell committed
161
}
162

163
bool sbbs_t::printtail(const char* fname, int lines, long mode, long org_cols, JSObject* obj)
164
{
rswindell's avatar
rswindell committed
165
	char*	buf;
166
	char	fpath[MAX_PATH+1];
rswindell's avatar
rswindell committed
167
	char*	p;
168
	FILE*	fp;
rswindell's avatar
rswindell committed
169
170
	int		file,cur=0;
	long	length,l;
171

172
173
	SAFECOPY(fpath, fname);
	fexistcase(fpath);
174
175
176
	if(mode&P_NOABORT) {
		if(online==ON_REMOTE) {
			rioctl(IOCM|ABORT);
rswindell's avatar
rswindell committed
177
178
179
180
			rioctl(IOCS|ABORT); 
		}
		sys_status&=~SS_ABORT; 
	}
rswindell's avatar
rswindell committed
181
182
	if(!(mode&P_NOCRLF) && row > 0) {
		newline();
rswindell's avatar
rswindell committed
183
	}
184
	if((fp=fnopen(&file,fpath,O_RDONLY|O_DENYNONE))==NULL) {
185
186
		if(!(mode&P_NOERROR)) {
			lprintf(LOG_NOTICE,"!Error %d (%s) opening: %s"
187
				,errno,strerror(errno),fpath);
188
			bputs(text[FileNotFound]);
189
			if(SYSOP) bputs(fpath);
190
191
192
			CRLF;
		}
		return false; 
rswindell's avatar
rswindell committed
193
	}
194
	length=(long)filelength(file);
rswindell's avatar
rswindell committed
195
	if(length<0) {
196
		fclose(fp);
197
		errormsg(WHERE,ERR_CHK,fpath,length);
198
		return false;
rswindell's avatar
rswindell committed
199
	}
200
201
202
203
	if(length > lines * PRINTFILE_MAX_LINE_LEN) {
		length = lines * PRINTFILE_MAX_LINE_LEN; 
		fseek(fp, -length, SEEK_END);
	}
deuce's avatar
deuce committed
204
	if((buf=(char*)malloc(length+1L))==NULL) {
205
		fclose(fp);
206
		errormsg(WHERE,ERR_ALLOC,fpath,length+1L);
207
		return false; 
rswindell's avatar
rswindell committed
208
	}
209
210
	l=fread(buf, sizeof(char), length, fp);
	fclose(fp);
rswindell's avatar
rswindell committed
211
	if(l!=length)
212
		errormsg(WHERE,ERR_READ,fpath,length);
rswindell's avatar
rswindell committed
213
214
215
216
217
218
219
220
221
222
223
224
	else {
		buf[l]=0;
		p=(buf+l)-1;
		if(*p==LF) p--;
		while(*p && p>buf) {
			if(*p==LF)
				cur++;
			if(cur>=lines) {
				p++;
				break; 
			}
			p--; 
rswindell's avatar
rswindell committed
225
		}
226
		putmsg(p,mode,org_cols, obj);
rswindell's avatar
rswindell committed
227
	}
228
229
	if(mode&P_NOABORT && online==ON_REMOTE) {
		SYNC;
rswindell's avatar
rswindell committed
230
231
		rioctl(IOSM|ABORT); 
	}
deuce's avatar
deuce committed
232
	free(buf);
233
	return true;
234
235
236
}

/****************************************************************************/
237
/* Displays a menu file (e.g. from the text/menu directory)                 */
238
239
/* Pass a code including wildcards (* or ?) to display a randomly-chosen	*/
/* file matching the pattern in 'code'										*/
240
/****************************************************************************/
241
bool sbbs_t::menu(const char *code, long mode, JSObject* obj)
242
{
243
244
245
    char path[MAX_PATH+1];
	const char *next= "msg";
	const char *last = "asc";
246

247
248
249
	if(strcspn(code, "*?") != strlen(code))
		return random_menu(code, mode, obj);

250
251
	sys_status&=~SS_ABORT;
	if(menu_file[0])
252
		SAFECOPY(path,menu_file);
253
	else {
rswindell's avatar
rswindell committed
254
		long term = term_supports();
255
256
257
258
259
260
261
262
263
264
265
266
		do {
			if((term&RIP) && menu_exists(code, "rip", path))
				break;
			if((term&(ANSI|COLOR)) == ANSI && menu_exists(code, "mon", path))
				break;
			if((term&ANSI) && menu_exists(code, "ans", path))
				break;
			if((term&PETSCII) && menu_exists(code, "seq", path))
				break;
			if(term&NO_EXASCII) {
				next = "asc";
				last = "msg";
rswindell's avatar
rswindell committed
267
			}
268
269
270
271
			if(menu_exists(code, next, path))
				break;
			menu_exists(code, last, path);
		} while(0);
272
	}
273

274
	mode |= P_OPENCLOSE | P_CPM_EOF;
275
276
	if(column == 0)
		mode |= P_NOCRLF;
277
	return printfile(path, mode, /* org_cols: */0, obj);
278
279
}

280
bool sbbs_t::menu_exists(const char *code, const char* ext, char* path)
281
{
282
283
284
	char pathbuf[MAX_PATH+1];
	if(path == NULL)
		path = pathbuf;
285

286
287
288
289
290
291
292
293
294
	if(menu_file[0]) {
		strncpy(path, menu_file, MAX_PATH);
		return fexistcase(path) ? true : false;
	}

	/* Either <menu>.asc or <menu>.msg is required */
	if(ext == NULL)
		return menu_exists(code, "asc", path)
			|| menu_exists(code, "msg", path);
295

296
297
298
299
300
301
302
	char prefix[MAX_PATH];
	if(isfullpath(code))
		SAFECOPY(prefix, code);
	else {
		backslash(menu_dir);
		SAFEPRINTF3(prefix, "%smenu/%s%s", cfg.text_dir, menu_dir, code);
	}
303
	safe_snprintf(path, MAX_PATH, "%s.%lucol.%s", prefix, cols, ext);
rswindell's avatar
rswindell committed
304
305
	if(fexistcase(path))
		return true;
306
	safe_snprintf(path, MAX_PATH, "%s.%s", prefix, ext);
deuce's avatar
deuce committed
307
308
	return fexistcase(path) ? true : false;
}
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339

/****************************************************************************/
/* Displays a random menu file (e.g. from the text/menu directory)          */
/****************************************************************************/
bool sbbs_t::random_menu(const char *name, long mode, JSObject* obj)
{
	char path[MAX_PATH + 1];
	glob_t g = {0};
	str_list_t names = NULL;

	SAFEPRINTF2(path, "%smenu/%s", cfg.text_dir, name);
	if(glob(path, GLOB_NOESCAPE|GLOB_MARK, NULL, &g) != 0) {
		return false;
	}
	for(size_t i = 0; i < g.gl_pathc; i++) {
		char* ext = getfext(g.gl_pathv[i]);
		if(ext == NULL)
			continue;
		*ext = 0;
		strListPush(&names, g.gl_pathv[i]);
	}
	globfree(&g);
	strListDedupe(&names, /* case_sensitive: */true);
	bool result = false;
	size_t i = sbbs_random(strListCount(names));
	if(menu_exists(names[i], NULL, path)) {
		result = menu(names[i], mode, obj);
	}
	strListFree(&names);
	return result;
}