prntfile.cpp 9.01 KB
Newer Older
1
2
3
4
5
6
/* Synchronet file print/display routines */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *																			*
 * 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										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
23
#include "utf8.h"
24
#include "petdefs.h"
25

26
#ifndef PRINTFILE_MAX_LINE_LEN
27
#define PRINTFILE_MAX_LINE_LEN (8*1024)
28
#endif
29
#ifndef PRINTFILE_MAX_FILE_LEN
30
#define PRINTFILE_MAX_FILE_LEN (2*1024*1024)
31
#endif
32

33
34
35
36
37
/****************************************************************************/
/* 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                                  */
/****************************************************************************/
38
bool sbbs_t::printfile(const char* fname, long mode, long org_cols, JSObject* obj)
39
{
40
	char* buf;
41
	char fpath[MAX_PATH+1];
42
	char* p;
rswindell's avatar
rswindell committed
43
	int file;
44
	BOOL rip=FALSE;
rswindell's avatar
rswindell committed
45
	long l,length,savcon=console;
46
47
	FILE *stream;

48
	SAFECOPY(fpath, fname);
49
	(void)fexistcase(fpath);
50
	p=getfext(fpath);
51
	if(p!=NULL) {
52
		if(stricmp(p,".rip")==0) {
rswindell's avatar
rswindell committed
53
			rip=TRUE;
rswindell's avatar
rswindell committed
54
			mode|=P_NOPAUSE;
55
56
		} else if(stricmp(p, ".seq") == 0) {
			mode |= P_PETSCII;
57
58
		} else if(stricmp(p, ".utf8") == 0) {
			mode |= P_UTF8;
rswindell's avatar
rswindell committed
59
		}
60
	}
61

62
	if(mode&P_NOABORT || rip) {
63
64
		if(online==ON_REMOTE && console&CON_R_ECHO) {
			rioctl(IOCM|ABORT);
rswindell's avatar
rswindell committed
65
66
67
68
			rioctl(IOCS|ABORT); 
		}
		sys_status&=~SS_ABORT; 
	}
69

70
	if((stream=fnopen(&file,fpath,O_RDONLY|O_DENYNONE))==NULL) {
71
72
		if(!(mode&P_NOERROR)) {
			lprintf(LOG_NOTICE,"!Error %d (%s) opening: %s"
73
				,errno,strerror(errno),fpath);
74
			bputs(text[FileNotFound]);
75
			if(SYSOP) bputs(fpath);
76
77
78
			CRLF;
		}
		return false; 
rswindell's avatar
rswindell committed
79
	}
80

81
	length=(long)filelength(file);
82
	if(length < 1) {
rswindell's avatar
rswindell committed
83
		fclose(stream);
84
85
86
87
88
		if(length < 0) {
			errormsg(WHERE,ERR_CHK,fpath,length);
			return false;
		}
		return true;
rswindell's avatar
rswindell committed
89
	}
90

91
92
93
94
	if(!(mode&P_NOCRLF) && row > 0 && !rip) {
		newline();
	}

95
	if((mode&P_OPENCLOSE) && length <= PRINTFILE_MAX_FILE_LEN) {
96
97
98
99
100
101
102
103
104
105
106
107
108
		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);
109
			putmsg(buf,mode,org_cols, obj);
110
111
112
		}
		free(buf);
	} else {	// Line-at-a-time mode
113
114
		ulong sys_status_sav = sys_status;
		enum output_rate output_rate = cur_output_rate;
115
		uint tmpatr = curatr;
116
117
		ulong orgcon = console;
		attr_sp = 0;	/* clear any saved attributes */
118
119
		if(!(mode&P_SAVEATR))
			attr(LIGHTGRAY);
120
121
		if(mode&P_NOPAUSE)
			sys_status |= SS_PAUSEOFF;
122
123
124
125
126
127
128
129
		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()) {
130
			if(fgets(buf, length + 1, stream) == NULL)
131
132
133
				break;
			if((mode&P_UTF8) && !term_supports(UTF8))
				utf8_normalize_str(buf);
134
			if(putmsgfrag(buf, mode, org_cols, obj) != '\0') // early-EOF?
135
				break;
136
137
		}
		free(buf);
rswindell's avatar
rswindell committed
138
		fclose(stream);
139
140
		if(!(mode&P_SAVEATR)) {
			console = orgcon;
141
			attr(tmpatr);
142
143
144
145
146
147
148
149
150
151
		}
		if(!(mode&P_NOATCODES) && cur_output_rate != output_rate)
			set_output_rate(output_rate);
		if(mode&P_PETSCII)
			outcom(PETSCII_UPPERLOWER);
		attr_sp=0;	/* clear any saved attributes */

		/* Restore original settings of Forced Pause On/Off */
		sys_status &= ~(SS_PAUSEOFF|SS_PAUSEON);
		sys_status |= (sys_status_sav&(SS_PAUSEOFF|SS_PAUSEON));
rswindell's avatar
rswindell committed
152
	}
153

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

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

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

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

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

251
252
	sys_status&=~SS_ABORT;
	if(menu_file[0])
253
		SAFECOPY(path,menu_file);
254
	else {
rswindell's avatar
rswindell committed
255
		long term = term_supports();
256
257
258
259
260
261
262
263
264
265
266
267
		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
268
			}
269
270
			if(menu_exists(code, next, path))
				break;
271
272
			if(!menu_exists(code, last, path))
				return false;
273
		} while(0);
274
	}
275

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

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

288
289
290
291
292
293
294
295
296
	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);
297

298
299
300
301
302
303
	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);
304
305
		FULLPATH(path, prefix, MAX_PATH);
		SAFECOPY(prefix, path);
306
	}
307
	safe_snprintf(path, MAX_PATH, "%s.%lucol.%s", prefix, cols, ext);
rswindell's avatar
rswindell committed
308
309
	if(fexistcase(path))
		return true;
310
	safe_snprintf(path, MAX_PATH, "%s.%s", prefix, ext);
deuce's avatar
deuce committed
311
312
	return fexistcase(path) ? true : false;
}
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327

/****************************************************************************/
/* 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++) {
328
		char* ext = strchr(g.gl_pathv[i], '.'); // intentionally not using getfext() - issue #380
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
		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;
}